Compare commits
3 Commits
v1.6.0-bet
...
v1.8.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
| 36944c71bf | |||
| 91c0a2d9d9 | |||
| 0ff0c0d55e |
@@ -1,113 +1,250 @@
|
||||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
require('vendor/autoload.php');
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
|
||||
<!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>Aufgabenmonitor</title>
|
||||
<html lang="de">
|
||||
<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>Aufgabenmonitor</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">
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
refreshAufgabenTable();
|
||||
|
||||
});
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css">
|
||||
<link href="css/sticky-footer.css" rel="stylesheet">
|
||||
|
||||
function refreshAufgabenTable(){
|
||||
$('#AufgabenTableHolder').load('sources/getAufgabenTable.php', function(){
|
||||
setTimeout(refreshAufgabenTable, 30000);
|
||||
});
|
||||
}
|
||||
// function refreshCheckInTable(){
|
||||
// $('#getCheckInTableHolder').load('sources/getCheckInTable.php', function(){
|
||||
// setTimeout(refreshCheckInTable, 5000);
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body style="background-color: black;">
|
||||
<style>
|
||||
body { background-color: #000; }
|
||||
.tableFixHead { overflow:auto; }
|
||||
.tableFixHead thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background-color: #212529; /* .table-dark head */
|
||||
}
|
||||
.table-dark td, .table-dark th { vertical-align: middle; }
|
||||
</style>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg">
|
||||
<h2 class="text-light">Aufgaben</h2>
|
||||
<table class="table table-dark">
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</header>
|
||||
<script src="scripts/jquery-3.5.1.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container-fluid py-3">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h2 class="text-light mb-3">Aufgaben</h2>
|
||||
|
||||
<!-- SCROLL CONTAINER -->
|
||||
<div id="aufgaben-scroll" class="tableFixHead">
|
||||
<table id="aufgaben-table" class="table table-dark table-striped table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width:6%">#</th>
|
||||
<th scope="col" style="width:24%">Bearbeiter</th>
|
||||
<th scope="col" style="width:40%">Aufgabe</th>
|
||||
<th scope="col" style="width:15%">Zieldatum</th>
|
||||
<th scope="col" style="width:15%">Priorität</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="AufgabenTableHolder">
|
||||
<!-- wird via AJAX gefüllt -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /SCROLL CONTAINER -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- === Scroll/Reload Script (wie bei CheckIn/CheckOut) === -->
|
||||
<script type="text/javascript">
|
||||
/* ===========================
|
||||
Feature-Toggle aus PHP
|
||||
=========================== */
|
||||
const SCROLL_FLAGS = {
|
||||
aufgaben: <?php echo (defined('EnableScrollingAufgaben') && EnableScrollingAufgaben) ? 'true' : 'false'; ?>
|
||||
};
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
|
||||
<!-- 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>
|
||||
function isEnabled() { return !!SCROLL_FLAGS.aufgaben; }
|
||||
|
||||
/* ===========================
|
||||
Höhe der Scroll-Container
|
||||
- Voller Viewport ab Oberkante
|
||||
=========================== */
|
||||
function sizeScrollContainers() {
|
||||
const node = document.querySelector('#aufgaben-scroll');
|
||||
if (!node) return;
|
||||
const rect = node.getBoundingClientRect();
|
||||
const bottomMargin = 16;
|
||||
const minH = 120;
|
||||
const available = window.innerHeight - rect.top - bottomMargin;
|
||||
node.style.maxHeight = Math.max(minH, available) + '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 (!isEnabled()) 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 st = ensureState(scEl);
|
||||
st.loopHeight = 0;
|
||||
|
||||
if (!isEnabled()) 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 (!isEnabled()) 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 (!isEnabled()) { stopAutoScroll($scroller); return; }
|
||||
const st = ensureState(el);
|
||||
if (st.userActive) return;
|
||||
if (st.loopHeight > 0) startAutoScroll($scroller, 1, 80);
|
||||
else stopAutoScroll($scroller);
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Smart AJAX Reload
|
||||
=========================== */
|
||||
function smartLoad($scroller, $table, $target, url, intervalMs) {
|
||||
const scEl = $scroller.get(0);
|
||||
const st = ensureState(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 () {
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
|
||||
if (st.loopHeight > 0 && isEnabled()) {
|
||||
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);
|
||||
}
|
||||
|
||||
setTimeout(() => smartLoad($scroller, $table, $target, url, intervalMs), intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Initialisierung
|
||||
=========================== */
|
||||
$(document).ready(function () {
|
||||
sizeScrollContainers();
|
||||
$(window).on('resize', function () {
|
||||
sizeScrollContainers();
|
||||
const $scroller = $('#aufgaben-scroll');
|
||||
const $table = $('#aufgaben-table');
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
maybeStartAutoScroll($scroller);
|
||||
});
|
||||
|
||||
// Guards nur aktivieren, wenn Scrolling erlaubt
|
||||
if (isEnabled()) attachScrollGuards($('#aufgaben-scroll'));
|
||||
|
||||
// Erstladen
|
||||
(function init() {
|
||||
const $scroller = $('#aufgaben-scroll');
|
||||
const $table = $('#aufgaben-table');
|
||||
const $target = $('#AufgabenTableHolder');
|
||||
const url = 'sources/getAufgabenTable.php';
|
||||
const interval = 30000;
|
||||
|
||||
$target.load(url, function () {
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
maybeStartAutoScroll($scroller);
|
||||
setTimeout(() => smartLoad($scroller, $table, $target, url, interval), interval);
|
||||
});
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
// (Optional – hier nicht benötigt, aber gelassen falls du später etwas anzeigst)
|
||||
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;
|
||||
$hours = floor($timestring / 3600);
|
||||
$mins = floor($timestring / 60 % 60);
|
||||
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
||||
return $timeFormat;
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -22,53 +22,241 @@ $Epi = new Epirent();
|
||||
<link href="css/sticky-footer.css" rel="stylesheet">
|
||||
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script type="text/javascript">
|
||||
// Dynamische Höhe: Wrapper exakt bis Viewport-Unterkante
|
||||
function sizeScrollContainers() {
|
||||
$('.tableFixHead').each(function () {
|
||||
const rect = this.getBoundingClientRect();
|
||||
const bottomMargin = 16; // kleiner Abstand zum Rand
|
||||
const available = window.innerHeight - rect.top - bottomMargin;
|
||||
this.style.maxHeight = (available > 120 ? available : 120) + 'px';
|
||||
});
|
||||
}
|
||||
<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'; ?>
|
||||
};
|
||||
|
||||
// Lädt tbody via AJAX und erhält die Scrollposition relativ zum unteren Rand
|
||||
function smartLoad($scroller, $target, url, intervalMs) {
|
||||
const scroller = $scroller.get(0);
|
||||
// Abstand vom unteren Rand merken (wichtiger als absolute scrollTop)
|
||||
const fromBottomBefore = scroller.scrollHeight - scroller.clientHeight - scroller.scrollTop;
|
||||
// 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']
|
||||
]);
|
||||
|
||||
$target.load(url, function () {
|
||||
// Nach dem Ersetzen: dieselbe Distanz zum unteren Rand wiederherstellen
|
||||
const newScrollTop = scroller.scrollHeight - scroller.clientHeight - fromBottomBefore;
|
||||
// Begrenzen, falls weniger Inhalt
|
||||
scroller.scrollTop = Math.max(0, newScrollTop);
|
||||
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', 5000, 'checkout');
|
||||
initOne('#checkin-scroll', '#checkin-table', '#getCheckInTableHolder', 'sources/getCheckInTable.php', 5000, 'checkin');
|
||||
initOne('#aufgaben-scroll', '#aufgaben-table', '#AufgabenTableHolder', 'sources/getAufgabenTable.php', 30000, 'aufgaben');
|
||||
});
|
||||
</script>
|
||||
|
||||
// Nächstes Update planen
|
||||
setTimeout(function () {
|
||||
smartLoad($scroller, $target, url, intervalMs);
|
||||
}, intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
sizeScrollContainers();
|
||||
$(window).on('resize', sizeScrollContainers);
|
||||
|
||||
// Initial laden + Auto-Refresh, jeweils eigener Scroller
|
||||
smartLoad($('#checkout-scroll'), $('#getCheckOutTableHolder'), 'sources/getCheckOutTable.php', 5000);
|
||||
smartLoad($('#checkin-scroll'), $('#getCheckInTableHolder'), 'sources/getCheckInTable.php', 5000);
|
||||
smartLoad($('#aufgaben-scroll'), $('#AufgabenTableHolder'), 'sources/getAufgabenTable.php', 30000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Scroll-Wrapper je Tabelle */
|
||||
@@ -98,7 +286,7 @@ $Epi = new Epirent();
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="background-color: black;">
|
||||
<body style="margin-bottom:0px; background-color: black;">
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
@@ -111,25 +299,31 @@ $Epi = new Epirent();
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
<th scope="col">
|
||||
|
||||
<?php
|
||||
if (!HideDispoTimes) {
|
||||
echo "Dispo-Start";
|
||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
||||
echo '<th scope="col">';
|
||||
}
|
||||
if (UsePackingNoteDateForCheckout) {
|
||||
echo "<br><i>Packen-Start</i></th>";
|
||||
} else {
|
||||
echo "<br><i>VB-Start</i></th>";
|
||||
}
|
||||
if (!HideCheckInTimeOnCheckout) {
|
||||
if(!HideDispoTimes){
|
||||
echo "<th scope='col'>Dispo-Ende<br><i>VB-Ende</i></th>";
|
||||
}else{
|
||||
echo "<th scope='col'><i>VB-Ende</i></th>";
|
||||
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@@ -152,24 +346,31 @@ $Epi = new Epirent();
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
|
||||
<?php
|
||||
if (!HideCheckOutTimeOnCheckin) {
|
||||
echo "<th scope='col'>Dispo-Start<br><i>RP-Start</i></th>";
|
||||
}
|
||||
|
||||
if(!HideDispoTimes){
|
||||
echo "<th scope='col'>Dispo-Ende";
|
||||
}else{
|
||||
echo "<th scope='col'>";
|
||||
}
|
||||
if (UsePackingNoteDateForCheckin) {
|
||||
echo "<br><i>Rücklieferung</i></th>";
|
||||
} else {
|
||||
echo "<br><i>RP-Ende</i></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>';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
|
||||
|
||||
@@ -218,7 +419,6 @@ if (UsePackingNoteDateForCheckin) {
|
||||
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
|
||||
<!-- 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>
|
||||
|
||||
512
Packmonitor.php
512
Packmonitor.php
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
|
||||
|
||||
require('config.php');
|
||||
require('EpiApi.php');
|
||||
require('vendor/autoload.php');
|
||||
@@ -12,180 +9,383 @@ $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>
|
||||
<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>
|
||||
<!-- 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>
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<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'; ?>
|
||||
};
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
refreshCheckOutTable();
|
||||
refreshCheckInTable();
|
||||
});
|
||||
const SCROLLER_KEYS = new Map([
|
||||
['checkout-scroll', 'checkout'],
|
||||
['checkin-scroll', 'checkin']
|
||||
]);
|
||||
|
||||
function refreshCheckOutTable() {
|
||||
$('#getCheckOutTableHolder').load('sources/getCheckOutTable.php', function () {
|
||||
setTimeout(refreshCheckOutTable, 5000);
|
||||
});
|
||||
}
|
||||
function refreshCheckInTable() {
|
||||
$('#getCheckInTableHolder').load('sources/getCheckInTable.php', function () {
|
||||
setTimeout(refreshCheckInTable, 5000);
|
||||
});
|
||||
}
|
||||
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;
|
||||
|
||||
</script>
|
||||
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);
|
||||
|
||||
</head>
|
||||
<body style="background-color: black;">
|
||||
const bottomMargin = 16;
|
||||
const minH = 120;
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg">
|
||||
<h2 class="text-light">Check-Out
|
||||
<?php
|
||||
if (CheckOut_FutureDays != -1) {
|
||||
echo "in den nächsten " . CheckOut_FutureDays . " Tagen";
|
||||
}
|
||||
?></h2>
|
||||
<table class="table table-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
<th scope="col">
|
||||
<?php
|
||||
if (!HideDispoTimes) {
|
||||
echo "Dispo-Start";
|
||||
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;
|
||||
}
|
||||
if (UsePackingNoteDateForCheckout) {
|
||||
echo "<br><i>Packen-Start</i></th>";
|
||||
} else {
|
||||
echo "<br><i>VB-Start</i></th>";
|
||||
}
|
||||
if (!HideCheckInTimeOnCheckout) {
|
||||
if(!HideDispoTimes){
|
||||
echo "<th scope='col'>Dispo-Ende<br><i>VB-Ende</i></th>";
|
||||
}else{
|
||||
echo "<th scope='col'><i>VB-Ende</i></th>";
|
||||
}
|
||||
|
||||
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 class="col-lg">
|
||||
<h2 class="text-light">Check-In
|
||||
<?php
|
||||
if (CheckIn_FutureDays != -1) {
|
||||
echo "in den nächsten " . CheckIn_FutureDays . " Tagen";
|
||||
}
|
||||
?></h2>
|
||||
<table class="table table-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
|
||||
<?php
|
||||
if (!HideCheckOutTimeOnCheckin) {
|
||||
echo "<th scope='col'>Dispo-Start<br><i>RP-Start</i></th>";
|
||||
}
|
||||
|
||||
if(!HideDispoTimes){
|
||||
echo "<th scope='col'>Dispo-Ende";
|
||||
}else{
|
||||
echo "<th scope='col'>";
|
||||
}
|
||||
if (UsePackingNoteDateForCheckin) {
|
||||
echo "<br><i>Rücklieferung</i></th>";
|
||||
} else {
|
||||
echo "<br><i>RP-Ende</i></th>";
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
|
||||
<th scope="col">Status</th>
|
||||
<?php
|
||||
if(ShowShippingIcons){
|
||||
echo "<th scope='col'>Shipping</th>";
|
||||
}
|
||||
?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="getCheckInTableHolder">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
|
||||
<!-- 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>
|
||||
<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);
|
||||
$secs = floor($timestring % 60);
|
||||
|
||||
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
||||
|
||||
return $timeFormat;
|
||||
$hours = floor($timestring / 3600);
|
||||
$mins = floor($timestring / 60 % 60);
|
||||
return sprintf('%02d:%02d', $hours, $mins);
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
413
dist/editconfig.php
vendored
Normal file
413
dist/editconfig.php
vendored
Normal file
@@ -0,0 +1,413 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Config-Editor für EPI Webview
|
||||
* - Liest ../config.php (falls vorhanden) und ../example.config.php
|
||||
* - Zeigt editierbare Felder für alle Keys aus example.config.php
|
||||
* - Zeigt veraltete (nur in config.php existierende) Keys rot & disabled
|
||||
* - Speichert Werte zurück nach ../config.php
|
||||
*/
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
// ---- Dateipfade anpassen falls nötig ----
|
||||
$CONFIG_FILE = realpath(__DIR__ . '/../config.php') ?: (__DIR__ . '/../config.php');
|
||||
$EXAMPLE_FILE = realpath(__DIR__ . '/../example.config.php') ?: (__DIR__ . '/../example.config.php');
|
||||
|
||||
// ---- Hilfsfunktionen: Parsing ----
|
||||
function parse_define_file(string $file): array {
|
||||
if (!is_file($file)) return ['defines' => [], 'order' => [], 'ui' => []];
|
||||
|
||||
$lines = file($file, FILE_IGNORE_NEW_LINES);
|
||||
$defines = [];
|
||||
$order = [];
|
||||
$ui = [];
|
||||
|
||||
// BOM entfernen, falls vorhanden
|
||||
if (isset($lines[0])) {
|
||||
$lines[0] = preg_replace('/^\xEF\xBB\xBF/', '', $lines[0]);
|
||||
}
|
||||
|
||||
// Regex
|
||||
$reDefine = '/^\s*define\(\s*([\'"])([^\'"]+)\1\s*,\s*(.+?)\s*\)\s*;\s*(?:(?:\/\/|#)\s*(.*))?$/u';
|
||||
$reSection = '/^\s*\/\/\s*@section\s*:\s*(.+)\s*$/iu';
|
||||
$reHR = '/^\s*\/\/\s*@hr\s*$/iu';
|
||||
$reNote = '/^\s*\/\/\s*@note\s*:\s*(.+)\s*$/iu';
|
||||
|
||||
foreach ($lines as $ln) {
|
||||
// Meta-Kommentare zuerst prüfen (UI-Struktur)
|
||||
if (preg_match($reSection, $ln, $m)) {
|
||||
$ui[] = ['kind' => 'section', 'title' => trim($m[1])];
|
||||
continue;
|
||||
}
|
||||
if (preg_match($reHR, $ln)) {
|
||||
$ui[] = ['kind' => 'hr'];
|
||||
continue;
|
||||
}
|
||||
if (preg_match($reNote, $ln, $m)) {
|
||||
$ui[] = ['kind' => 'note', 'text' => trim($m[1])];
|
||||
continue;
|
||||
}
|
||||
|
||||
// define(…) Zeilen
|
||||
if (!preg_match($reDefine, $ln, $m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $m[2];
|
||||
$rawValueExpr = trim($m[3]);
|
||||
$inlineC = isset($m[4]) ? trim($m[4]) : '';
|
||||
|
||||
// Typ bestimmen
|
||||
$type = 'string';
|
||||
$valForForm = '';
|
||||
$raw = rtrim($rawValueExpr, ',');
|
||||
|
||||
if (preg_match('/^(true|false)$/i', $raw)) {
|
||||
$type = 'bool';
|
||||
$valForForm = (strtolower($raw) === 'true');
|
||||
} elseif (preg_match('/^[+-]?\d+$/', $raw)) {
|
||||
$type = 'int';
|
||||
$valForForm = (int)$raw;
|
||||
} elseif (preg_match('/^([\'"])(.*)\1$/s', $raw, $mm)) {
|
||||
$type = 'string';
|
||||
$valForForm = stripcslashes($mm[2]);
|
||||
} else {
|
||||
$type = 'string';
|
||||
$valForForm = $raw;
|
||||
}
|
||||
|
||||
$defines[$name] = [
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'value' => $valForForm,
|
||||
'raw' => $rawValueExpr,
|
||||
'comment' => $inlineC,
|
||||
'line' => $ln,
|
||||
];
|
||||
$order[] = $name;
|
||||
// Wichtig: define auch in die UI-Reihenfolge aufnehmen
|
||||
$ui[] = ['kind' => 'define', 'name' => $name];
|
||||
}
|
||||
|
||||
return ['defines' => $defines, 'order' => $order, 'ui' => $ui];
|
||||
}
|
||||
|
||||
|
||||
function php_export_define_value(string $type, $value): string {
|
||||
// Baut den rechten Teil von define('X', <HIER>);
|
||||
if ($type === 'bool') {
|
||||
return $value ? 'true' : 'false';
|
||||
}
|
||||
if ($type === 'int') {
|
||||
return (string)intval($value);
|
||||
}
|
||||
// String:
|
||||
// einfache Quotes nehmen und escapen
|
||||
$escaped = str_replace("'", "\\'", (string)$value);
|
||||
return "'" . $escaped . "'";
|
||||
}
|
||||
|
||||
function h(?string $s): string { return htmlspecialchars((string)$s ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
|
||||
|
||||
// ---- Dateien parsen ----
|
||||
$example = parse_define_file($EXAMPLE_FILE);
|
||||
$current = parse_define_file($CONFIG_FILE);
|
||||
|
||||
// Keys klassifizieren
|
||||
$exampleKeys = array_keys($example['defines']);
|
||||
$currentKeys = array_keys($current['defines']);
|
||||
|
||||
$deprecatedKeys = array_values(array_diff($currentKeys, $exampleKeys)); // nur in config.php
|
||||
$newKeys = array_values(array_diff($exampleKeys, $currentKeys)); // nur in example (neu)
|
||||
$sharedKeys = array_values(array_intersect($exampleKeys, $currentKeys));
|
||||
|
||||
// Beim Rendern: Reihenfolge aus example beibehalten
|
||||
$renderKeys = $example['order'];
|
||||
|
||||
// ---- POST: Speichern ----
|
||||
$message = '';
|
||||
$err = '';
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['__save_config']) && isset($_POST['cfg']) && is_array($_POST['cfg'])) {
|
||||
|
||||
$incoming = $_POST['cfg']; // ['NAME' => valueString/checkbox etc.]
|
||||
|
||||
// Build neuer config.php Inhalt:
|
||||
$out = [];
|
||||
$out[] = "<?php";
|
||||
$out[] = "// Diese Datei wurde durch den Epi Webview Config-Editor erzeugt/aktualisiert.";
|
||||
$out[] = "// Bearbeite Werte bevorzugt über die Weboberfläche.";
|
||||
$out[] = "";
|
||||
|
||||
// --- Inhalt in der Reihenfolge & Struktur der example.config.php ---
|
||||
foreach ($example['ui'] as $item) {
|
||||
if ($item['kind'] === 'section') {
|
||||
$out[] = "// @section: " . $item['title'];
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] === 'hr') {
|
||||
$out[] = "// @hr";
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] === 'note') {
|
||||
$out[] = "// @note: " . $item['text'];
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] !== 'define') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$label = $item['name'];
|
||||
$ex = $example['defines'][$label] ?? null;
|
||||
if (!$ex) continue;
|
||||
|
||||
$type = $ex['type']; // Typ an example orientieren
|
||||
$tooltip = $ex['comment'] ?? '';
|
||||
|
||||
// Wert aus POST übernehmen (Checkbox-Fix: existiert das Feld?)
|
||||
if ($type === 'bool') {
|
||||
$bool = array_key_exists($label, $incoming);
|
||||
$valueForDefine = php_export_define_value('bool', $bool);
|
||||
} elseif ($type === 'int') {
|
||||
$raw = $incoming[$label] ?? '';
|
||||
$valueForDefine = php_export_define_value('int', $raw);
|
||||
} else {
|
||||
$raw = $incoming[$label] ?? '';
|
||||
$valueForDefine = php_export_define_value('string', $raw);
|
||||
}
|
||||
|
||||
$line = "define('{$label}', {$valueForDefine});";
|
||||
if (!empty($tooltip)) {
|
||||
$line .= " // " . $tooltip; // Kommentare als Tooltip auch in config.php mitführen
|
||||
}
|
||||
$out[] = $line;
|
||||
}
|
||||
|
||||
// Deprecated Block (nur in aktueller config.php vorhanden, nicht in example)
|
||||
if (!empty($deprecatedKeys)) {
|
||||
$out[] = "";
|
||||
$out[] = "// --- Veraltete/entfernte Parameter (nur Referenz, werden nicht mehr genutzt) ---";
|
||||
foreach ($deprecatedKeys as $key) {
|
||||
$c = $current['defines'][$key];
|
||||
$valueForDefine = php_export_define_value($c['type'], $c['value']);
|
||||
$line = "// define('{$c['name']}', {$valueForDefine});";
|
||||
if (!empty($c['comment'])) {
|
||||
$line .= " // " . $c['comment'];
|
||||
}
|
||||
$out[] = $line;
|
||||
}
|
||||
}
|
||||
|
||||
$out[] = "?>";
|
||||
$content = implode("\n", $out) . "\n";
|
||||
|
||||
try {
|
||||
if (false === file_put_contents($CONFIG_FILE, $content)) {
|
||||
$err = "Konnte config.php nicht schreiben: " . h($CONFIG_FILE);
|
||||
} else {
|
||||
$message = "Konfiguration gespeichert.";
|
||||
// Nach dem Speichern neu parsen, damit UI aktuell ist
|
||||
$current = parse_define_file($CONFIG_FILE);
|
||||
$currentKeys = array_keys($current['defines']);
|
||||
$deprecatedKeys = array_values(array_diff($currentKeys, $exampleKeys));
|
||||
$sharedKeys = array_values(array_intersect($exampleKeys, $currentKeys));
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$err = "Fehler beim Schreiben: " . h($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---- Werte für Formular vorbereiten ----
|
||||
// Für jeden example-Key: Formularwert = aus aktueller config (falls vorhanden), sonst example default
|
||||
function valueForForm(array $example, array $current, string $key) {
|
||||
$ex = $example['defines'][$key] ?? null;
|
||||
$cu = $current['defines'][$key] ?? null;
|
||||
$type = $ex ? $ex['type'] : ($cu ? $cu['type'] : 'string');
|
||||
$comment = $ex && $ex['comment'] !== '' ? $ex['comment'] : ($cu['comment'] ?? '');
|
||||
|
||||
if ($cu) {
|
||||
$val = $cu['value'];
|
||||
} else {
|
||||
$val = $ex['value'];
|
||||
}
|
||||
return [$type, $val, $comment];
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="description" content="Konfiguration bearbeiten" />
|
||||
<meta name="author" content="" />
|
||||
<title>Konfiguration – Epi Webview</title>
|
||||
|
||||
<link href="css/styles.css" rel="stylesheet" />
|
||||
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/js/all.min.js" crossorigin="anonymous"></script>
|
||||
<script src="js/jquery-3.5.1.min.js"></script>
|
||||
|
||||
<style>
|
||||
.deprecated { color: #c62828; }
|
||||
.cfg-help { margin-left: .35rem; cursor: help; }
|
||||
.form-group { margin-bottom: .75rem; }
|
||||
.small-muted { font-size:.85rem; color:#6c757d; }
|
||||
.checkbox-inline { display:flex; align-items:center; gap:.5rem; }
|
||||
.cfg-label { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
// Tooltips (Bootstrap 4)
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
// Side UI nachladen wie in deinem Template
|
||||
loadSidenav();
|
||||
loadFooter();
|
||||
});
|
||||
function loadSidenav(){
|
||||
$('#layoutSidenav_nav').load('../sources/getSidenav.php');
|
||||
}
|
||||
function loadFooter(){
|
||||
$('#footerholder').load('../sources/getFooter.php');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="sb-nav-fixed">
|
||||
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
|
||||
<a class="navbar-brand" href="index.php">Epi Webview</a>
|
||||
<button class="btn btn-link btn-sm order-1 order-lg-0" id="sidebarToggle" type="button"><i class="fas fa-bars"></i></button>
|
||||
<ul class="navbar-nav ml-auto ml-md-0"></ul>
|
||||
</nav>
|
||||
|
||||
<div id="layoutSidenav">
|
||||
<div id="layoutSidenav_nav"></div>
|
||||
<div id="layoutSidenav_content">
|
||||
<main>
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4">Konfiguration</h1>
|
||||
<ol class="breadcrumb mb-4">
|
||||
<li class="breadcrumb-item active">Dashboard / Konfiguration</li>
|
||||
</ol>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-success"><?= h($message) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($err): ?>
|
||||
<div class="alert alert-danger"><?= h($err) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-sliders-h mr-1"></i>
|
||||
Einstellungen
|
||||
<span class="small-muted">( <code><?=h(basename($CONFIG_FILE))?></code> Source: <code><?=h(basename($EXAMPLE_FILE))?></code>)</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<input type="hidden" name="__save_config" value="1" />
|
||||
|
||||
<?php foreach ($example['ui'] as $item): ?>
|
||||
<?php
|
||||
if ($item['kind'] === 'section') {
|
||||
echo '<h5 class="mt-3">'.h($item['title']).'</h5>';
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] === 'hr') {
|
||||
echo '<hr />';
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] === 'note') {
|
||||
echo '<p class="small-muted">'.h($item['text']).'</p>';
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] !== 'define') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = $item['name'];
|
||||
// Nur Keys rendern, die in example vorkommen
|
||||
if (!isset($example['defines'][$key])) continue;
|
||||
|
||||
[$type, $val, $tooltip] = valueForForm($example, $current, $key);
|
||||
?>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="cfg-label" for="cfg_<?=h($key)?>">
|
||||
<?= h($key) ?>
|
||||
<?php if ($tooltip): ?>
|
||||
<i class="far fa-question-circle cfg-help"
|
||||
data-toggle="tooltip" data-placement="right"
|
||||
title="<?=h($tooltip)?>"></i>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
|
||||
<?php if ($type === 'bool'): ?>
|
||||
<div class="checkbox-inline">
|
||||
<input type="checkbox"
|
||||
id="cfg_<?=h($key)?>"
|
||||
name="cfg[<?=h($key)?>]"
|
||||
value="1" <?= $val ? 'checked' : '' ?> />
|
||||
<span><?= $val ? 'Aktiviert' : 'Deaktiviert' ?></span>
|
||||
</div>
|
||||
<?php elseif ($type === 'int'): ?>
|
||||
<input type="number" step="1" class="form-control"
|
||||
id="cfg_<?=h($key)?>"
|
||||
name="cfg[<?=h($key)?>]"
|
||||
value="<?=h((string)$val)?>" />
|
||||
<?php else: ?>
|
||||
<input type="text" class="form-control"
|
||||
id="cfg_<?=h($key)?>"
|
||||
name="cfg[<?=h($key)?>]"
|
||||
value="<?=h((string)$val)?>" />
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if (!empty($deprecatedKeys)): ?>
|
||||
<hr />
|
||||
<h5 class="deprecated">Veraltete Parameter (nur in <code><?=h(basename($CONFIG_FILE))?></code>, nicht mehr in <code><?=h(basename($EXAMPLE_FILE))?></code>)</h5>
|
||||
<?php foreach ($deprecatedKeys as $key): ?>
|
||||
<?php $c = $current['defines'][$key]; ?>
|
||||
<div class="form-group">
|
||||
<label class="cfg-label deprecated" for="dep_<?=h($key)?>">
|
||||
<?= h($key) ?>
|
||||
<?php if (!empty($c['comment'])): ?>
|
||||
<i class="far fa-question-circle cfg-help" data-toggle="tooltip" data-placement="right" title="<?=h($c['comment'])?>"></i>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
<input type="text" class="form-control is-invalid" id="dep_<?=h($key)?>" value="<?=
|
||||
h($c['type']==='bool' ? ($c['value']?'true':'false') : (string)$c['value'])
|
||||
?>" disabled />
|
||||
<div class="invalid-feedback">Dieser Parameter ist in der aktuellen Version nicht mehr vorgesehen.</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-save mr-1"></i> Speichern</button>
|
||||
<span class="small-muted ml-2">Speichert nach <code><?=h($CONFIG_FILE)?></code></span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
<div id="footerholder"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script src="js/scripts.js"></script>
|
||||
<script>
|
||||
// Sidebar Toggle (falls benötigt)
|
||||
$('#sidebarToggle').on('click', function(){ $('body').toggleClass('sb-sidenav-toggled'); });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,38 +1,59 @@
|
||||
<?php
|
||||
//EPIRENT CONNECTION EINSTELLUNGEN//
|
||||
// @section: Epirent – Verbindung
|
||||
define('Epirent_Server', 'xxx');
|
||||
define('Epirent_Connectionprotocol', 'http');
|
||||
define('Epirent_Port', '8080');
|
||||
define('Epirent_Token', 'xxx');
|
||||
define('Epirent_Mandant', '0'); //NORMALERWEISE 0
|
||||
|
||||
//CREWBRAIN CONNECTION EINSTELLUNGEN-NUR AUSFÜLLEN WENN GEWÜNSCHT//
|
||||
// @hr
|
||||
// @section: Crewbrain Verbindung
|
||||
// @note: Nur ausfüllen wenn gewünscht
|
||||
define('CrewBrain_Username', 'xxx');
|
||||
define('CrewBrain_password', 'xxx');
|
||||
define('CrewBrain_Server', 'xxx.crewbrain.com');
|
||||
define('CrewBrain_Connectionprotocol', 'https');
|
||||
define('CrewBrain_TaskListID', 6);
|
||||
|
||||
//CREWBRAIN SPEZIFISCHE EINSTELLUNGEN (AUFGABENLISTE)//
|
||||
define('Enable_QR_Code_CrewBrainAufgaben', true);
|
||||
|
||||
//EPIRENT SPEZIFISCHE EINSTELLUNGEN (CHECK IN & CHECK OUT)
|
||||
// @hr
|
||||
// @section: Epirent-Spezifische Einstellungen
|
||||
define('Enable_QR_Code_CheckOut', false); //Zeigt statt der Packscheinnummer einen Scanbaren QR Code für den CheckOut an
|
||||
define('Enable_QR_Code_CheckIn', false); //Zeigt statt der Packscheinnummer einen Scanbaren QR Code für den CheckIn an
|
||||
define('Vorbereitungs_Zeitvariable', 'Packen'); //Name des zu verwendenden Zeitabschnitts, der Zusätzlich zur DispoZeit beim Check Out Angezeigt werden soll
|
||||
define('Rueckpacken_Zeitvariable', 'Rückpacken'); //Name des zu verwendenden Zeitabschnitts, der Zusätzlich zur DispoZeit beim Check In Angezeigt werden soll
|
||||
define('Nachbereitung_Zeitvariable', 'Rückpacken'); //Name des zu verwendenden Zeitabschnitts aus dem Auftrag, der die Nachbereitung angibt
|
||||
define('Rückpacken_Zeitvariable', 'Rückpacken'); //Name des zu verwendenden Zeitabschnitts aus dem Auftrag, der das Rückpacken angibt.
|
||||
define('UseDeliveredForCheckOutCompleted', true); //Nutzt statt dem erfolgreichen abschließen eines Packscheins durch ausbuchen das erfolgreiche Liefern (Geliefert != 00.00.00)
|
||||
define('EnableScrollingCheckOut', true); //Aktiviert das automatische Scrollen der CheckOut Liste
|
||||
define('EnableScrollingCheckIn', true); //Aktiviert das automatische Scrollen der CheckIn Liste
|
||||
define('EnableScrollingAufgaben', true); //Aktiviert das automatische Scrollen der Aufgabenliste
|
||||
// @note: -------------------- CheckOutRowMarkSource: Konfig Zusände --------------------
|
||||
// @note: 1 = $packingjob->date_start (Dispo Start)
|
||||
// @note: 2 = $VorbereitungsTimeDetail->date_start (Vorbereitung Start)
|
||||
// @note: 3 = $PackingNoteDetail->date_packing (Packen Zeit)
|
||||
// @note: 4 = $PackingNoteDetail->date_delivery (Delivery Zeit)
|
||||
define('CheckOutRowMarkSource', 4);
|
||||
|
||||
define('UsePackingNoteDateForCheckout', true); // Nutzt statt den Zeitabscnitten aus dem Auftrag die Informationen aus dem Packschein für den Checkout. Wenn die UseDispo Variablen false sind, werden diese Variablen für das Rowmarking genutzt falls "true".
|
||||
define('UsePackingNoteDateForCheckin', true); // Nutzt statt den Zeitabscnitten aus dem Auftrag die Informationen aus dem Packschein für den Checkout. Wenn die UseDispo Variablen false sind, werden diese Variablen für das Rowmarking genutzt falls "true".
|
||||
// @note: -------------------- CheckInRowMarkSource: Konfig Zusände --------------------
|
||||
// @note: 1 = $packingjob->date_end (Dispo Ende)
|
||||
// @note: 2 = $NachbereitungssTimeDetail->date_start (Nachbereitung Start)
|
||||
// @note: 3 = $RePackagingTimeDetail->date_start (Rückpacken Zeit AUS AUFTRAG)
|
||||
// @note: 4 = $PackingNoteDetail->date_redelivery (ReDelivery Zeit)
|
||||
define('CheckInRowMarkSource', 4);
|
||||
|
||||
define('CheckOut_UseDispoStartForRowMarking', false); //else: Use Same Variable as "Vorbereitung Zeitvariable" | Konfiguration, welche Zeit für die Zeilenmarkierung beim CheckOut Verwendet werden soll
|
||||
define('CheckIn_UseDispoEndForRowMarking', false); //else: Use Same Variable as "Rueckpacken Zeitvariable" | Konfiguration, welche Zeit für die Zeilenmarkierung beim Check In Verwendet werden soll
|
||||
|
||||
define('HideCheckInTimeOnCheckout', true); //Versteckt die CheckIn Zeit im Checkout
|
||||
define('HideCheckOutTimeOnCheckin', true); //Versteckt die CheckOut Zeit im CheckIn
|
||||
|
||||
define('HideDispoTimes', true); //Versteckt Dispo-Zeiten Komplett aus den Listen
|
||||
define('ShowCheckoutTimeOnCheckout', true); //Zeigt die Checkout Zeit im Checkout
|
||||
define('ShowVorbereitungTimeOnCheckout', true); //Zeigt die Vorbereitungs Zeitvariable im Checkout
|
||||
define('ShowPackagingTimeOnCheckout', true); //Zeigt die Packenzeit im Checkout
|
||||
define('ShowDeliveryTimeOnCheckout', true); //Zeigt die Lieferzeit im Checkout
|
||||
define('ShowTimesOnCheckout', true); //Aktiviert das anzeigen der Uhrzeit im Checkout
|
||||
// @hr
|
||||
define('ShowCheckInTimeOnCheckin', true); //Zeigt die CheckIn Zeit im CheckIn
|
||||
define('ShowNachbereitungTimeOnCheckin', true); //Zeigt die Nachbereitungs Zeitvariable im CheckIn
|
||||
define('ShowRePackagingTimeOnCheckin', true); //Zeigt die Rückpackzeit im Checkin <br><b>Achtung: Zeit nur Im Auftrag festlegbar</b>
|
||||
define('ShowReDeliveryTimeOnCheckin', true); //Zeigt die geplante Rücklieferung im Checkin
|
||||
define('ShowTimesOnCheckin', true); //Aktiviert das anzeigen der Uhrzeit im CheckIn
|
||||
|
||||
// @section: Epirent-Spezifische Einstellungen - Shipping
|
||||
define('ShowShippingIcons', true); //Zeigt Lieferung / Rücklieferungs Icons an
|
||||
define('KurierContainsText', 'Kurier'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als KURIER erkannt wird.
|
||||
define('SpeditionContainsText', 'Spedition'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als SPEDITION erkannt wird.
|
||||
@@ -46,8 +67,7 @@ define('ShippingOutOrganizedStatus', 'Hinversand OK'); //Packschein Status (Epir
|
||||
define('ShippingInOrganizedStatus', 'Rückversand OK'); //Packschein Status (EpirentDropdown) der Gematcht werden muss, dass der Rückversand Organisiert / Bestellt wurde
|
||||
define('ShippingOrganizedStatus', 'Versand OK'); //Packschein Status (EpirentDropdown) der Gematcht werden muss, dass der komplette Versand Organisiert / Bestellt wurde
|
||||
|
||||
define('UseDeliveredForCheckOutCompleted', true); //Nutzt statt dem erfolgreichen abschließen eines Packscheins durch ausbuchen das erfolgreiche Liefern (Geliefert != 00.00.00)
|
||||
|
||||
// @section: Epirent-Spezifische Einstellungen - Anzeigesortierung
|
||||
define('CheckOut_FutureDays', -1); // Konfiguration, wie viele Tage in der Zukunft der CheckOut angezeigt werden soll. '-1' zeigt alle an. Abhängig von der Variablen CheckOut_UseDispoStartForRowMarking
|
||||
define('CheckIn_FutureDays', -1); // Konfiguration, wie viele Tage in der Zukunft der CheckIn angezeigt werden soll. '-1' zeigt alle an. Abhängig von der Variablen CheckIn_UseDispoEndForRowMarking
|
||||
define('SortCheckOut', 2); // Konfiguration, welcher Datensatz für die Sortierung Verwendet werden soll. Möglichkeiten '1': Packscheinnummer / '2': Dispostart
|
||||
|
||||
@@ -10,215 +10,250 @@ use chillerlan\QRCode\{
|
||||
|
||||
require('../vendor/autoload.php');
|
||||
|
||||
|
||||
|
||||
$options = new QROptions([
|
||||
'imageBase64' => false,
|
||||
'qrCodeHeight' => 75,
|
||||
'qrCodeWidth' => 75,
|
||||
'version' => -1,
|
||||
'quietzoneSize' => 1
|
||||
]);
|
||||
|
||||
]);
|
||||
|
||||
$Epi = new Epirent();
|
||||
|
||||
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
|
||||
|
||||
$data_output = json_decode($result)->payload;
|
||||
|
||||
/** ---------- Helpers & Marking (analog Checkout) ---------- */
|
||||
const APP_TZ = 'Europe/Berlin';
|
||||
|
||||
function dt(?string $s): ?DateTimeImmutable {
|
||||
if (!$s) return null;
|
||||
try {
|
||||
return new DateTimeImmutable($s, new DateTimeZone(APP_TZ));
|
||||
} catch (Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function dayStart(DateTimeImmutable $d): DateTimeImmutable {
|
||||
return $d->setTime(0, 0, 0);
|
||||
}
|
||||
|
||||
function rowClassForDate(?DateTimeImmutable $markDate, ?DateTimeImmutable $today): string {
|
||||
if (!$markDate || !$today) return '';
|
||||
if ($markDate == $today) return 'text-dark bg-warning';
|
||||
if ($markDate < $today) return 'bg-danger';
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Row-Marking Quelle (Check-In):
|
||||
* 1 = $packingjob->date_end
|
||||
* 2 = $NachbereitungsTimeDetail->date_start
|
||||
* 3 = $RePackagingTimeDetail->date_start
|
||||
* 4 = $PackingNoteDetail->date_redelivery
|
||||
* Fallback: $packingjob->date_end
|
||||
*/
|
||||
function resolveRowMarkDateCheckIn(
|
||||
$packingjob,
|
||||
$NachbereitungsTimeDetail,
|
||||
$RePackagingTimeDetail,
|
||||
$PackingNoteDetail,
|
||||
int $source
|
||||
): ?DateTimeImmutable {
|
||||
$candidate = null;
|
||||
switch ($source) {
|
||||
case 1: $candidate = dt($packingjob->date_end ?? null); break;
|
||||
case 2: $candidate = dt($NachbereitungsTimeDetail->date_start ?? null); break;
|
||||
case 3: $candidate = dt($RePackagingTimeDetail->date_start ?? null); break;
|
||||
case 4: $candidate = dt($PackingNoteDetail->date_redelivery ?? null); break;
|
||||
default: $candidate = null; break;
|
||||
}
|
||||
if (!$candidate) {
|
||||
$candidate = dt($packingjob->date_end ?? null);
|
||||
}
|
||||
return $candidate ? dayStart($candidate) : null;
|
||||
}
|
||||
|
||||
function echoMarkedTimeLine(?string $dateStr, ?int $timeSeconds, DateTimeImmutable $today, $ReturnTime): void {
|
||||
if (!$dateStr) return;
|
||||
$d = dt($dateStr);
|
||||
if (!$d) return;
|
||||
|
||||
$mark = dayStart($d);
|
||||
$cls = rowClassForDate($mark, $today);
|
||||
|
||||
echo $cls ? "<span class=\"{$cls}\">" : "";
|
||||
echo date_format(new \DateTime($dateStr), 'd.m.Y');
|
||||
if ($ReturnTime) {
|
||||
echo " " . getTimeFromSeconds((string)$timeSeconds);
|
||||
}
|
||||
echo $cls ? "</span>" : "";
|
||||
}
|
||||
|
||||
/** ---------- Sortierung nach Ende ---------- */
|
||||
if (SortCheckIn == 2) {
|
||||
|
||||
|
||||
|
||||
// Prüfen, ob $data_output ein Array ist
|
||||
if (is_array($data_output)) {
|
||||
usort($data_output, function ($a, $b) {
|
||||
// Konvertiere time_start von Millisekunden in Sekunden
|
||||
$timeStartA = $a->time_end / 1000; // Zeit in Sekunden
|
||||
$timeStartB = $b->time_end / 1000;
|
||||
|
||||
// Kombiniere date_start mit time_start
|
||||
$datetimeA = strtotime($a->date_end) + $timeStartA;
|
||||
$datetimeB = strtotime($b->date_end) + $timeStartB;
|
||||
|
||||
// Vergleich für die Sortierung
|
||||
$timeEndA = $a->time_end / 1000;
|
||||
$timeEndB = $b->time_end / 1000;
|
||||
$datetimeA = strtotime($a->date_end) + $timeEndA;
|
||||
$datetimeB = strtotime($b->date_end) + $timeEndB;
|
||||
return $datetimeA <=> $datetimeB;
|
||||
});
|
||||
|
||||
// Sortierte Daten ausgeben oder weiterverarbeiten
|
||||
// print_r($data_output);
|
||||
} else {
|
||||
echo "Daten konnten nicht verarbeitet werden.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** ---------- Tabelle ---------- */
|
||||
foreach ($data_output as $packingjob) {
|
||||
|
||||
|
||||
|
||||
if ($packingjob->is_archived != true) {
|
||||
|
||||
//get OrderDetails
|
||||
// OrderDetails
|
||||
$result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);
|
||||
|
||||
$orderdetail_output = json_decode($result)->payload[0];
|
||||
|
||||
// get PackingNote Details
|
||||
// PackingNote Details
|
||||
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNote_data_output = json_decode($PackingNoteDetailResult)->payload[0];
|
||||
|
||||
|
||||
$NachbereitungsTimeDetail;
|
||||
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
|
||||
|
||||
// Zeit-Slots aus dem Schedule
|
||||
$NachbereitungsTimeDetail = null;
|
||||
$RePackagingTimeDetail = null;
|
||||
|
||||
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
||||
|
||||
|
||||
if (UsePackingNoteDateForCheckin) {
|
||||
|
||||
$tempTimeObject = new stdClass();
|
||||
$tempTimeObject->date_end = $PackingNote_data_output->date_redelivery;
|
||||
$tempTimeObject->time_end = $PackingNote_data_output->time_redelivery;
|
||||
print($PackingNote_data_output->time_redelivery);
|
||||
$NachbereitungsTimeDetail = $tempTimeObject;
|
||||
|
||||
} else {
|
||||
if ($scheduledetail->name == Rueckpacken_Zeitvariable) {
|
||||
if ($scheduledetail->name == Nachbereitung_Zeitvariable) {
|
||||
$NachbereitungsTimeDetail = $scheduledetail;
|
||||
}
|
||||
if ($scheduledetail->name == Rückpacken_Zeitvariable) {
|
||||
$RePackagingTimeDetail = $scheduledetail;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
//End Of get Order Details
|
||||
|
||||
|
||||
|
||||
if (CheckIn_UseDispoEndForRowMarking || ($NachbereitungsTimeDetail->date_start == null)) {
|
||||
$date = new DateTime($packingjob->date_end);
|
||||
} else {
|
||||
$date = new DateTime($NachbereitungsTimeDetail->date_start);
|
||||
// --- Row-Marking bestimmen (konfigurierbar, analog Checkout) ---
|
||||
$today = dayStart(new DateTimeImmutable('today', new DateTimeZone(APP_TZ)));
|
||||
$limit = null;
|
||||
if (CheckIn_FutureDays != -1) {
|
||||
$limit = $today->modify('+' . (int)CheckIn_FutureDays . ' day');
|
||||
}
|
||||
$date->setTime(0, 0, 0);
|
||||
$today = new DateTime();
|
||||
$today->setTime(0, 0, 0);
|
||||
$todayFilter = new DateTime();
|
||||
$todayFilter->setTime(0, 0, 0);
|
||||
if (CheckIn_FutureDays == -1 || $date <= ($todayFilter->modify('+' . CheckIn_FutureDays . ' day'))) {
|
||||
//prüfe, ob entweder unbegrenzte (-1) Anzeige Aktiv ist, oder das Datum kleiner oder Gleich heute + Zukunftsspanne ist
|
||||
|
||||
if ($date == $today) {
|
||||
// Override-Flag wie bei Checkout: Dispo-Ende erzwingen
|
||||
$source = CheckIn_UseDispoEndForRowMarking ? 1 : (int)CheckInRowMarkSource;
|
||||
|
||||
echo "<tr class='text-dark bg-warning'>";
|
||||
} else if ($date < $today) {
|
||||
echo "<tr class=' bg-danger'>";
|
||||
} else {
|
||||
echo "<tr>";
|
||||
}
|
||||
$markDate = resolveRowMarkDateCheckIn(
|
||||
$packingjob,
|
||||
$NachbereitungsTimeDetail,
|
||||
$RePackagingTimeDetail,
|
||||
$PackingNoteDetail,
|
||||
$source
|
||||
);
|
||||
|
||||
if (CheckIn_FutureDays == -1 || ($markDate && (!$limit || $markDate <= $limit))) {
|
||||
$trClass = rowClassForDate($markDate, $today);
|
||||
echo $trClass ? "<tr class='{$trClass}'>" : "<tr>";
|
||||
|
||||
// QR / Kopfspalten
|
||||
if (Enable_QR_Code_CheckIn) {
|
||||
echo "<td>" . '<div style="width: 5vb;">' . (new QRCode($options))->render($packingjob->packingnote_no) . "</div></td>";
|
||||
echo "<td><div style=\"width: 5vb;\">" . (new QRCode($options))->render($packingjob->packingnote_no) . "</div></td>";
|
||||
} else {
|
||||
echo "<td>" . $packingjob->packingnote_no . "</td>";
|
||||
}
|
||||
echo "<td>" . $packingjob->contact->name . "</td>";
|
||||
echo "<td>" . $packingjob->event . "</td>";
|
||||
if(!HideCheckOutTimeOnCheckin){
|
||||
if ($NachbereitungsTimeDetail->date_start != null) {
|
||||
|
||||
// Zeitspalte öffnen, wenn mindestens eine Anzeige aktiv ist
|
||||
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin) {
|
||||
echo "<td>";
|
||||
if(!HideDispoTimes){
|
||||
echo "<small>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</small><br>";
|
||||
}
|
||||
echo "<i>" . date_format(new \DateTime($NachbereitungsTimeDetail->date_start), 'd.m.Y') . " " . getTimeFromSeconds($NachbereitungsTimeDetail->time_start) . "</i></td>";
|
||||
|
||||
} else if(!HideDispoTimes){
|
||||
echo "<td>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</td>";
|
||||
}
|
||||
|
||||
// Dispo-Ende (Check-In Termin) — ENDE korrekt verwendet
|
||||
if (ShowCheckInTimeOnCheckin && $packingjob->date_end != null) {
|
||||
echoMarkedTimeLine($packingjob->date_end, (int)$packingjob->time_end, $today, ShowTimesOnCheckin);
|
||||
}
|
||||
if ($NachbereitungsTimeDetail->date_end != null) {
|
||||
echo "<td>";
|
||||
if(!HideDispoTimes){
|
||||
echo "<small>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</small><br>";
|
||||
}
|
||||
echo "<i>" . date_format(new \DateTime($NachbereitungsTimeDetail->date_end), 'd.m.Y') . " " . getTimeFromSeconds($NachbereitungsTimeDetail->time_end) . "</i></td>";
|
||||
|
||||
|
||||
} else if(!HideDispoTimes){
|
||||
echo "<td>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</td>";
|
||||
|
||||
// Nachbereitung (START) — wie von dir bereits genutzt
|
||||
if (ShowNachbereitungTimeOnCheckin && $NachbereitungsTimeDetail && $NachbereitungsTimeDetail->date_start != null) {
|
||||
if (ShowCheckInTimeOnCheckin) echo "<br>";
|
||||
echoMarkedTimeLine($NachbereitungsTimeDetail->date_start, (int)$NachbereitungsTimeDetail->time_start, $today, ShowTimesOnCheckin);
|
||||
}
|
||||
|
||||
// Rückpacken (START)
|
||||
if (ShowRePackagingTimeOnCheckin && $RePackagingTimeDetail && $RePackagingTimeDetail->date_start != null) {
|
||||
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin) echo "<br>";
|
||||
echoMarkedTimeLine($RePackagingTimeDetail->date_start, (int)$RePackagingTimeDetail->time_start, $today, ShowTimesOnCheckin);
|
||||
}
|
||||
|
||||
// Rücklieferung (REDELIVERY) — delivery ↔ redelivery gespiegelt
|
||||
if (ShowReDeliveryTimeOnCheckin && $PackingNoteDetail && $PackingNoteDetail->date_redelivery != null) {
|
||||
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin) echo "<br>";
|
||||
echoMarkedTimeLine($PackingNoteDetail->date_redelivery, (int)$PackingNoteDetail->time_redelivery, $today, ShowTimesOnCheckin);
|
||||
}
|
||||
|
||||
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin) {
|
||||
echo "</td>";
|
||||
}
|
||||
|
||||
// Fortschritt
|
||||
echo "<td>";
|
||||
|
||||
|
||||
|
||||
if ($packingjob->is_all_in ==0) {
|
||||
if ($packingjob->is_all_in == 0) {
|
||||
echo "<span class='badge badge-success'>";
|
||||
} else {
|
||||
|
||||
echo '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: '.(( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) / ($packingjob->pieces_sum_total - $packingjob ->is_all_out)) *100 .'%" aria-valuenow="' .( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) . '" aria-valuemin="0" aria-valuemax="' . ($packingjob->pieces_sum_total - $packingjob ->is_all_out). '"></div></div>';
|
||||
|
||||
echo '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: ' .
|
||||
(( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) / ($packingjob->pieces_sum_total - $packingjob->is_all_out)) * 100 .
|
||||
'%" aria-valuenow="' . ( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) .
|
||||
'" aria-valuemin="0" aria-valuemax="' . ($packingjob->pieces_sum_total - $packingjob->is_all_out) . '"></div></div>';
|
||||
echo "<span class='badge badge-info'>";
|
||||
}
|
||||
|
||||
echo ( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) . "/" . ($packingjob->pieces_sum_total - $packingjob ->is_all_out) . " (" . $packingjob->pieces_sum_total.")";
|
||||
echo ( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) . "/" . ($packingjob->pieces_sum_total - $packingjob->is_all_out) . " (" . $packingjob->pieces_sum_total . ")";
|
||||
echo "</span></td>";
|
||||
|
||||
if(ShowShippingIcons){
|
||||
|
||||
if(UseShippingStatus){
|
||||
if(($PackingNote_data_output->status==ShippingInOrganizedStatus)||($PackingNote_data_output->status==ShippingOrganizedStatus)){
|
||||
echo "<td style='color:#66FF00; text-align:center;'>";
|
||||
}else{
|
||||
echo "<td style='text-align:center;'>";
|
||||
|
||||
}
|
||||
|
||||
if($PackingNote_data_output->is_self_redeliver){
|
||||
echo '<i class="fa-solid fa-person-walking fa-lg"></i>';
|
||||
} else{
|
||||
if(preg_match('/'.KurierContainsText.'/i',$PackingNote_data_output->shipping_in)){
|
||||
echo '<i class="fa-solid fa-hand-holding-dollar fa-lg"></i>';
|
||||
// Shipping-Icons (inbound)
|
||||
if (ShowShippingIcons) {
|
||||
if (UseShippingStatus) {
|
||||
if (($PackingNoteDetail->status == ShippingInOrganizedStatus) || ($PackingNoteDetail->status == ShippingOrganizedStatus)) {
|
||||
echo "<td style='color:#66FF00; text-align:center;'>";
|
||||
} else {
|
||||
echo "<td style='text-align:center;'>";
|
||||
}
|
||||
if(preg_match('/'.SpeditionContainsText.'/i',$PackingNote_data_output->shipping_in)){
|
||||
echo '<i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if(preg_match('/'.DHLContainsText.'/i',$PackingNote_data_output->shipping_in)){
|
||||
echo '<i class="fa-brands fa-dhl fa-2xl"></i>';
|
||||
}
|
||||
if(preg_match('/'.LKWContainsText.'/i',$PackingNote_data_output->shipping_in)){
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if(preg_match('/'.TransporterContainsText.'/i',$PackingNote_data_output->shipping_in)){
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-van-shuttle fa-lg"></i>';
|
||||
}
|
||||
if(preg_match('/'.PKWContainsText.'/i',$PackingNote_data_output->shipping_in)){
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-car fa-lg"></i>';
|
||||
|
||||
if ($PackingNoteDetail->is_self_redeliver) {
|
||||
echo '<i class="fa-solid fa-person-walking fa-lg"></i>';
|
||||
} else {
|
||||
if (preg_match('/' . KurierContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-solid fa-hand-holding-dollar fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . SpeditionContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . DHLContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-brands fa-dhl fa-2xl"></i>';
|
||||
}
|
||||
if (preg_match('/' . LKWContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . TransporterContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-van-shuttle fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . PKWContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-car fa-lg"></i>';
|
||||
}
|
||||
}
|
||||
echo "</td>";
|
||||
}
|
||||
echo "</td>";
|
||||
|
||||
}
|
||||
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTimeFromSeconds(string $timestring) {
|
||||
|
||||
$hours = floor($timestring / 3600);
|
||||
$mins = floor($timestring / 60 % 60);
|
||||
$secs = floor($timestring % 60);
|
||||
|
||||
$mins = floor($timestring / 60 % 60);
|
||||
$secs = floor($timestring % 60);
|
||||
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
||||
|
||||
return $timeFormat;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
|
||||
error_reporting(E_ALL);
|
||||
require('../config.php');
|
||||
require('../EpiApi.php');
|
||||
@@ -19,23 +20,64 @@ $options = new QROptions([
|
||||
'quietzoneSize' => 1
|
||||
]);
|
||||
|
||||
|
||||
$Epi = new Epirent();
|
||||
|
||||
if(UseDeliveredForCheckOutCompleted){
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
|
||||
const APP_TZ = 'Europe/Berlin';
|
||||
|
||||
}else{
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isci=False&cl=' . Epirent_Mandant);
|
||||
/** Hilfsfunktionen für die Row-Marking-Logik (ohne weitere Änderungen am Rest) */
|
||||
function dt(?string $s): ?DateTimeImmutable {
|
||||
if (!$s)
|
||||
return null;
|
||||
try {
|
||||
return new DateTimeImmutable($s, new DateTimeZone(APP_TZ));
|
||||
} catch (Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function dayStart(DateTimeImmutable $d): DateTimeImmutable {
|
||||
return $d->setTime(0, 0, 0);
|
||||
}
|
||||
|
||||
function resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, int $source): ?DateTimeImmutable {
|
||||
$candidate = null;
|
||||
switch ($source) {
|
||||
case 1: $candidate = dt($packingjob->date_start ?? null);
|
||||
break;
|
||||
case 2: $candidate = dt($VorbereitungsTimeDetail->date_start ?? null);
|
||||
break;
|
||||
case 3: $candidate = dt($PackingNoteDetail->date_packing ?? null);
|
||||
break;
|
||||
case 4: $candidate = dt($PackingNoteDetail->date_delivery ?? null);
|
||||
break;
|
||||
default: $candidate = null;
|
||||
break;
|
||||
}
|
||||
if (!$candidate) {
|
||||
$candidate = dt($packingjob->date_start ?? null);
|
||||
}
|
||||
return $candidate ? dayStart($candidate) : null;
|
||||
}
|
||||
|
||||
function rowClassForDate(?DateTimeImmutable $markDate, ?DateTimeImmutable $today): string {
|
||||
if (!$markDate || !$today)
|
||||
return '';
|
||||
if ($markDate == $today)
|
||||
return 'text-dark bg-warning';
|
||||
if ($markDate < $today)
|
||||
return 'bg-danger';
|
||||
return '';
|
||||
}
|
||||
|
||||
if (UseDeliveredForCheckOutCompleted) {
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
|
||||
} else {
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isci=False&cl=' . Epirent_Mandant);
|
||||
}
|
||||
$data_output = json_decode($result)->payload;
|
||||
|
||||
|
||||
|
||||
if (SortCheckOut == 2) {
|
||||
|
||||
|
||||
|
||||
// Prüfen, ob $data_output ein Array ist
|
||||
if (is_array($data_output)) {
|
||||
usort($data_output, function ($a, $b) {
|
||||
@@ -60,70 +102,54 @@ if (SortCheckOut == 2) {
|
||||
|
||||
foreach ($data_output as $packingjob) {
|
||||
|
||||
$PackingNoteDetail = null;
|
||||
|
||||
$PackingNote_data_output;
|
||||
|
||||
if($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted){
|
||||
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNote_data_output = json_decode($PackingNoteDetailResult)->payload[0];
|
||||
}
|
||||
|
||||
if ($packingjob->is_archived != true && (!UseDeliveredForCheckOutCompleted || ($PackingNote_data_output->date_delivered == "0000-00-00" && $PackingNote_data_output->time_delivered == 0))) {
|
||||
if ($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted) {
|
||||
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
|
||||
}
|
||||
|
||||
if (
|
||||
$packingjob->is_archived != true
|
||||
&& (
|
||||
!UseDeliveredForCheckOutCompleted
|
||||
|| ($PackingNoteDetail->date_delivered !== "0000-00-00" || (int)$PackingNoteDetail->time_delivered !== 0)
|
||||
)
|
||||
&& (int)$PackingNoteDetail->is_all_out !== 0
|
||||
) {
|
||||
//get OrderDetails
|
||||
$result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);
|
||||
|
||||
$orderdetail_output = json_decode($result)->payload[0];
|
||||
|
||||
// get PackingNote Details
|
||||
if(!UseDeliveredForCheckOutCompleted){
|
||||
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNote_data_output = json_decode($PackingNoteDetailResult)->payload[0];
|
||||
// get PackingNote Details, aber nur wenn nicht schon vor der schleife geholt.
|
||||
if (!UseDeliveredForCheckOutCompleted) {
|
||||
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
|
||||
}
|
||||
$VorbereitungsTimeDetail;
|
||||
|
||||
$VorbereitungsTimeDetail = null;
|
||||
|
||||
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
||||
|
||||
|
||||
if (UsePackingNoteDateForCheckout) {
|
||||
|
||||
$tempTimeObject = new stdClass();
|
||||
$tempTimeObject->date_start = $PackingNote_data_output->date_packing;
|
||||
$tempTimeObject->time_start = $PackingNote_data_output->time_packing;
|
||||
|
||||
$VorbereitungsTimeDetail = $tempTimeObject;
|
||||
} else {
|
||||
if ($scheduledetail->name == Vorbereitungs_Zeitvariable) {
|
||||
$VorbereitungsTimeDetail = $scheduledetail;
|
||||
}
|
||||
if ($scheduledetail->name == Vorbereitungs_Zeitvariable) {
|
||||
$VorbereitungsTimeDetail = $scheduledetail;
|
||||
}
|
||||
}
|
||||
//End Of get Order Details
|
||||
|
||||
if (CheckOut_UseDispoStartForRowMarking || ($VorbereitungsTimeDetail->date_start == null)) {
|
||||
$date = new DateTime($packingjob->date_start);
|
||||
} else if(UseDeliveredForCheckOutCompleted ){
|
||||
{
|
||||
$date = new DateTime($PackingNote_data_output->date_delivery);
|
||||
|
||||
}
|
||||
}else{
|
||||
$date = new DateTime($VorbereitungsTimeDetail->date_start);
|
||||
// --- Row-Marking Datum bestimmen (konfigurierbar) ---
|
||||
$today = dayStart(new DateTimeImmutable('today', new DateTimeZone(APP_TZ)));
|
||||
$todayFilter = $today; // für die Window-Berechnung
|
||||
$limit = null;
|
||||
if (CheckOut_FutureDays != -1) {
|
||||
$limit = $todayFilter->modify('+' . (int) CheckOut_FutureDays . ' day');
|
||||
}
|
||||
|
||||
$date->setTime(0, 0, 0);
|
||||
$today = new DateTime();
|
||||
$today->setTime(0, 0, 0);
|
||||
$markDate = resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, (int) CheckOutRowMarkSource);
|
||||
|
||||
$todayFilter = new DateTime();
|
||||
$todayFilter->setTime(0, 0, 0);
|
||||
if (CheckOut_FutureDays == -1 || $date <= ($todayFilter->modify('+' . CheckOut_FutureDays . ' day'))) {
|
||||
//prüfe, ob entweder unbegrenzte (-1) Anzeige Aktiv ist, oder das Datum kleiner oder Gleich heute + Zukunftsspanne ist
|
||||
if ($date == $today) {
|
||||
if (CheckOut_FutureDays == -1 || ($markDate && $markDate <= $limit)) {
|
||||
$trClass = rowClassForDate($markDate, $today);
|
||||
|
||||
echo "<tr class='text-dark bg-warning'>";
|
||||
} else if ($date < $today) {
|
||||
echo "<tr class=' bg-danger'>";
|
||||
if ($trClass) {
|
||||
echo "<tr class='{$trClass}'>";
|
||||
} else {
|
||||
echo "<tr>";
|
||||
}
|
||||
@@ -136,61 +162,37 @@ if($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted){
|
||||
echo "<td>" . $packingjob->contact->name . "</td>";
|
||||
echo "<td>" . $packingjob->event . "</td>";
|
||||
|
||||
|
||||
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout) { echo "<td>";}
|
||||
|
||||
|
||||
if(UseDeliveredForCheckOutCompleted){
|
||||
|
||||
if (CheckOut_UseDispoStartForRowMarking || ($VorbereitungsTimeDetail->date_start == null)) {
|
||||
$date = new DateTime($packingjob->date_start);
|
||||
} else {
|
||||
$date = new DateTime($VorbereitungsTimeDetail->date_start);
|
||||
}
|
||||
|
||||
if ($date == $today) {
|
||||
|
||||
echo "<td class='text-dark bg-warning'>";
|
||||
} else if ($date < $today) {
|
||||
echo "<td class=' bg-danger'>";
|
||||
} else {
|
||||
echo "<td>";
|
||||
if (ShowCheckoutTimeOnCheckout && $packingjob->date_start != null) {
|
||||
echoMarkedTimeLine($packingjob->date_start, (int) $packingjob->time_start, $today, ShowTimesOnCheckout);
|
||||
}
|
||||
if (ShowVorbereitungTimeOnCheckout && $VorbereitungsTimeDetail && $VorbereitungsTimeDetail->date_start != null) {
|
||||
if (ShowCheckoutTimeOnCheckout) {
|
||||
echo "<br>";
|
||||
}
|
||||
echoMarkedTimeLine($VorbereitungsTimeDetail->date_start, (int) $VorbereitungsTimeDetail->time_start, $today, ShowTimesOnCheckout);
|
||||
}
|
||||
if (ShowPackagingTimeOnCheckout && $PackingNoteDetail && $PackingNoteDetail->date_packing != null) {
|
||||
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout) {
|
||||
echo "<br>";
|
||||
}
|
||||
echoMarkedTimeLine($PackingNoteDetail->date_packing, (int) $PackingNoteDetail->time_packing, $today, ShowTimesOnCheckout);
|
||||
}
|
||||
if (ShowDeliveryTimeOnCheckout && $PackingNoteDetail && $PackingNoteDetail->date_delivery != null) {
|
||||
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout) {
|
||||
echo "<br>";
|
||||
}
|
||||
echoMarkedTimeLine($PackingNoteDetail->date_delivery, (int) $PackingNoteDetail->time_delivery, $today, ShowTimesOnCheckout);
|
||||
}
|
||||
|
||||
|
||||
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout) {
|
||||
echo "</td>";
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
echo "<td>";
|
||||
|
||||
}
|
||||
|
||||
if ($VorbereitungsTimeDetail->date_start != null) {
|
||||
|
||||
if(!HideDispoTimes){
|
||||
echo "<small>".date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</small><br>";
|
||||
}
|
||||
echo "<i>" . date_format(new \DateTime($VorbereitungsTimeDetail->date_start), 'd.m.Y') . " " . getTimeFromSeconds($VorbereitungsTimeDetail->time_start) . "</i></td>";
|
||||
|
||||
|
||||
|
||||
} else if(!HideDispoTimes){
|
||||
date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</td>";
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!HideCheckInTimeOnCheckout) {
|
||||
if (($VorbereitungsTimeDetail->date_end != null)) {
|
||||
|
||||
if(!HideDispoTimes){
|
||||
|
||||
echo "<small>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</small><br>";
|
||||
}
|
||||
echo "<i>" . date_format(new \DateTime($VorbereitungsTimeDetail->date_end), 'd.m.Y') . " " . getTimeFromSeconds($VorbereitungsTimeDetail->time_end) . "</i></td>";
|
||||
|
||||
} else if(!HideDispoTimes){
|
||||
echo date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</td>";
|
||||
}
|
||||
}
|
||||
echo "<td>";
|
||||
|
||||
|
||||
if ($packingjob->is_all_out == 0) {
|
||||
echo "<span class='badge badge-success'>";
|
||||
@@ -203,52 +205,65 @@ if($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted){
|
||||
|
||||
echo ($packingjob->pieces_sum_total - abs($packingjob->is_all_out)) . "/" . $packingjob->pieces_sum_total;
|
||||
echo "</span></td>";
|
||||
|
||||
if(ShowShippingIcons){
|
||||
|
||||
if(UseShippingStatus){
|
||||
if(($PackingNote_data_output->status==ShippingOutOrganizedStatus)||($PackingNote_data_output->status==ShippingOrganizedStatus)){
|
||||
echo "<td style='color:#66FF00; text-align:center;'>";
|
||||
}else{
|
||||
echo "<td style='text-align:center;'>";
|
||||
|
||||
}
|
||||
|
||||
if($PackingNote_data_output->is_self_pickup){
|
||||
echo '<i class="fa-solid fa-person-walking fa-lg"></i>';
|
||||
} else{
|
||||
if(preg_match('/'.KurierContainsText.'/i',$PackingNote_data_output->shipping_out)){
|
||||
echo '<i class="fa-solid fa-hand-holding-dollar fa-lg"></i>';
|
||||
if (ShowShippingIcons) {
|
||||
|
||||
if (UseShippingStatus) {
|
||||
if (($PackingNoteDetail->status == ShippingOutOrganizedStatus) || ($PackingNoteDetail->status == ShippingOrganizedStatus)) {
|
||||
echo "<td style='color:#66FF00; text-align:center;'>";
|
||||
} else {
|
||||
echo "<td style='text-align:center;'>";
|
||||
}
|
||||
if(preg_match('/'.SpeditionContainsText.'/i',$PackingNote_data_output->shipping_out)){
|
||||
echo '<i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if(preg_match('/'.DHLContainsText.'/i',$PackingNote_data_output->shipping_out)){
|
||||
echo '<i class="fa-brands fa-dhl fa-2xl"></i>';
|
||||
}
|
||||
if(preg_match('/'.LKWContainsText.'/i',$PackingNote_data_output->shipping_out)){
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if(preg_match('/'.TransporterContainsText.'/i',$PackingNote_data_output->shipping_out)){
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-van-shuttle fa-lg"></i>';
|
||||
}
|
||||
if(preg_match('/'.PKWContainsText.'/i',$PackingNote_data_output->shipping_out)){
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-car fa-lg"></i>';
|
||||
|
||||
if ($PackingNoteDetail->is_self_pickup) {
|
||||
echo '<i class="fa-solid fa-person-walking fa-lg"></i>';
|
||||
} else {
|
||||
if (preg_match('/' . KurierContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-solid fa-hand-holding-dollar fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . SpeditionContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . DHLContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-brands fa-dhl fa-2xl"></i>';
|
||||
}
|
||||
if (preg_match('/' . LKWContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . TransporterContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-van-shuttle fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . PKWContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-car fa-lg"></i>';
|
||||
}
|
||||
}
|
||||
echo "</td>";
|
||||
}
|
||||
echo "</td>";
|
||||
|
||||
}
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function echoMarkedTimeLine(?string $dateStr, ?int $timeSeconds, DateTimeImmutable $today, $ReturnTime): void {
|
||||
if (!$dateStr)
|
||||
return;
|
||||
$d = dt($dateStr);
|
||||
if (!$d)
|
||||
return;
|
||||
|
||||
$mark = dayStart($d);
|
||||
$cls = rowClassForDate($mark, $today);
|
||||
|
||||
echo $cls ? "<span class=\"{$cls}\">" : "";
|
||||
echo date_format(new \DateTime($dateStr), 'd.m.Y');
|
||||
if($ReturnTime){echo " ".getTimeFromSeconds((string) $timeSeconds);}
|
||||
echo $cls ? "</span>" : "";
|
||||
}
|
||||
|
||||
function getTimeFromSeconds(string $timestring) {
|
||||
|
||||
$hours = floor($timestring / 3600);
|
||||
@@ -260,4 +275,4 @@ function getTimeFromSeconds(string $timestring) {
|
||||
return $timeFormat;
|
||||
}
|
||||
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
<div class="sb-nav-link-icon"><i class="fas fa-list"></i></div>
|
||||
Pack- & Aufgabenmonitor
|
||||
</a>
|
||||
<div class="sb-sidenav-menu-heading">Config</div>
|
||||
<a class="nav-link" href=editconfig.php>
|
||||
<div class="sb-nav-link-icon"><i class="fas fa-tachometer-alt"></i></div>
|
||||
Config
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sb-sidenav-footer">
|
||||
|
||||
Reference in New Issue
Block a user