251 lines
8.5 KiB
PHP
251 lines
8.5 KiB
PHP
<?php
|
||
require('config.php');
|
||
require('vendor/autoload.php');
|
||
?>
|
||
<!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">
|
||
<title>Aufgabenmonitor</title>
|
||
|
||
<!-- Bootstrap -->
|
||
<link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css">
|
||
<link href="css/sticky-footer.css" rel="stylesheet">
|
||
|
||
<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>
|
||
|
||
<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'; ?>
|
||
};
|
||
|
||
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);
|
||
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
||
return $timeFormat;
|
||
}
|
||
?>
|