5 Commits

Author SHA1 Message Date
91c0a2d9d9 - Endzeiten bei CheckOut und Startzeiten bei CheckIn komplett entfernt
- Zeiten einzeln Ein- und Ausblendbar gemacht
- Quelle für Zeilenfarbe einstellbar gemacht
- Zeiten werden jetzt bei erreichen bzw überschreiten immer markiert (nur die Zeit)
- Nicht benötigte Spalten werden automatisch ausgeblendet
- Config Datei Ausgedünnt (Bitte komplette Epirent-Settings (nicht Login) Sektion ersetzen
2025-10-10 09:36:11 +02:00
0ff0c0d55e Endlos Scrollen der Listen implementiert (falls notwendig) 2025-10-09 20:42:19 +02:00
1358e17ac3 config Datei angepasst 2025-10-09 18:22:07 +02:00
4ce1578bb5 Readme angepasst 2025-10-09 18:21:25 +02:00
72dac40753 V1.6.0: Shipping hinzugefügt, Erlaube Pachscheintermine als Packtermine, Feature: Shipping abgeschlossen kann nun als CheckOut Bedingung genommen werden (vorher: Fertig gepackt) 2025-10-09 18:20:17 +02:00
32 changed files with 26003 additions and 398 deletions

View File

@@ -1,11 +1,9 @@
<?php
require('config.php');
require('EpiApi.php');
require('vendor/autoload.php');
$Epi = new Epirent();
?>
@@ -21,155 +19,334 @@ $Epi = new Epirent();
<!-- 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]-->
<link href="css/sticky-footer.css" rel="stylesheet">
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
<script type="text/javascript">
// Dynamische Höhe: Wrapper exakt bis Viewport-Unterkante
// === Höhe der Scroll-Container bis Viewport-Ende anpassen ===
function sizeScrollContainers() {
$('.tableFixHead').each(function () {
const rect = this.getBoundingClientRect();
const bottomMargin = 16; // kleiner Abstand zum Rand
const bottomMargin = 16;
const available = window.innerHeight - rect.top - bottomMargin;
this.style.maxHeight = (available > 120 ? available : 120) + 'px';
});
}
// 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;
// === 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;
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 ===
// Klont den ersten <tbody> als __loopClone ans Tabellenende, wenn Inhalt > Sichtbereich.
function buildSeamlessLoop($table, $scroller) {
$table.find('tbody.__loopClone').remove();
const $main = $table.find('tbody').first();
const scEl = $scroller.get(0);
const st = ensureState(scEl);
if ($main.length === 0 || $main.children().length === 0) {
st.loopHeight = 0;
return;
}
const mainH = $main.get(0).offsetHeight;
const needScroll = mainH > scEl.clientHeight + 1;
if (!needScroll) {
st.loopHeight = 0;
return;
}
const $clone = $main.clone(false, false).addClass('__loopClone').attr('aria-hidden', 'true');
$table.append($clone);
st.loopHeight = mainH; // Höhe des Original-Inhalts als Loop-Länge
}
// === Auto-Scroll (nahtlos) ===
function startAutoScroll($scroller, speedPx = 1, stepMs = 40) {
const el = $scroller.get(0);
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;
// Nahtlos: sobald wir das Ende des Original-Blocks überschreiten, ziehen wir loopHeight ab
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);
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 ===
// Wir merken die Position modulo loopHeight; nach dem Reload setzen wir proportional um.
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 () {
// 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);
// Loop neu bewerten/aufbauen
buildSeamlessLoop($table, $scroller);
// Nächstes Update planen
setTimeout(function () {
smartLoad($scroller, $target, url, intervalMs);
}, intervalMs);
if (st.loopHeight > 0) {
// Neue relative Position setzen (proportional)
const newPos = Math.floor(posRatio * st.loopHeight);
if (Math.abs(scEl.scrollTop - newPos) > 1) {
// rAF für flüssiges Setzen außerhalb des load()-Layouts
requestAnimationFrame(() => { scEl.scrollTop = newPos; });
}
// Auto-Scroll (wieder) starten
stopAutoScroll($scroller);
startAutoScroll($scroller, 1, 80);
} else {
// kein Overflow -> ganz oben und Auto-Scroll aus
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', sizeScrollContainers);
$(window).on('resize', function () {
sizeScrollContainers();
// Nach Layoutwechsel neu entscheiden
['#checkout', '#checkin', '#aufgaben'].forEach(prefix => {
const $scroller = $(`${prefix}-scroll`);
const $table = $(`${prefix}-table`);
buildSeamlessLoop($table, $scroller);
maybeStartAutoScroll($scroller);
});
});
// 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);
// Guards pro Scroller
attachScrollGuards($('#checkout-scroll'));
attachScrollGuards($('#checkin-scroll'));
attachScrollGuards($('#aufgaben-scroll'));
// Erstladen je Tabelle: laden -> Loop bauen -> ggf. Auto-Scroll -> zyklisch refreshen
function initOne(scrollerSel, tableSel, tbodySel, url, ms) {
const $scroller = $(scrollerSel);
const $table = $(tableSel);
const $target = $(tbodySel);
$target.load(url, function () {
buildSeamlessLoop($table, $scroller);
maybeStartAutoScroll($scroller);
setTimeout(() => smartLoad($scroller, $table, $target, url, ms), ms);
});
}
initOne('#checkout-scroll', '#checkout-table', '#getCheckOutTableHolder', 'sources/getCheckOutTable.php', 5000);
initOne('#checkin-scroll', '#checkin-table', '#getCheckInTableHolder', 'sources/getCheckInTable.php', 5000);
initOne('#aufgaben-scroll', '#aufgaben-table', '#AufgabenTableHolder', 'sources/getAufgabenTable.php', 30000);
});
</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;
}
/* 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 */
}
/* 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 */
}
/* Saubere Hintergrundfarbe für sticky Header (Bootstrap .table-dark) */
.table-dark thead th {
background-color: #212529; /* gleiche Farbe wie .table-dark header */
}
/* Optional: dünne Trennlinien */
.table-dark tbody tr + tr td {
border-top: 1px solid rgba(255,255,255,.08);
}
</style>
/* Optional: dünne Trennlinien */
.table-dark tbody tr + tr td {
border-top: 1px solid rgba(255,255,255,.08);
}
</style>
</head>
<body style="background-color: black;">
<body style="margin-bottom:0px; background-color: black;">
<div class="container-fluid">
<div class="row">
<div class="col-sm">
<h2 class="text-light">Check-Out</h2>
<div class="tableFixHead" id="checkout-scroll">
<table class="table table-dark mb-0" id="checkout-table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Kunde</th>
<th scope="col">Event</th>
<th scope="col">Dispo-Start<br><i>VB-Start</i></th>
<?php
<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>
if(!HideCheckInTimeOnCheckout){
echo "<th scope='col'>Dispo-Ende<br><i>VB-Ende</i></th>";
}
?>
<th scope="col">Status</th>
</tr>
</thead>
<tbody id="getCheckOutTableHolder"></tbody>
</table>
</div>
</div>
<?php
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
echo '<th scope="col">';
}
<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(!HideCheckOutTimeOnCheckin){
echo "<th scope='col'>Dispo-Start<br><i>RP-Start</i></th>";
}
?>
<th scope="col">Dispo-Ende<br><i>RP-Ende</i></th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody id="getCheckInTableHolder"></tbody>
</table>
</div>
</div>
<div class="col-sm">
<h2 class="text-light">Aufgaben</h2>
<div class="tableFixHead" id="aufgaben-scroll">
<table class="table table-dark mb-0" id="aufgaben-table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Bearbeiter</th>
<th scope="col">Aufgabe</th>
<th scope="col">Zieldatum</th>
<th scope="col">Priorität</th>
</tr>
</thead>
<tbody id="AufgabenTableHolder"></tbody>
</table>
</div>
</div>
if(ShowCheckoutTimeOnCheckout){
echo "Dispo-Start";
}
if(ShowVorbereitungTimeOnCheckout){
if(ShowCheckoutTimeOnCheckout){echo "<br>";}
echo "VB-Start";
}
if(ShowPackagingTimeOnCheckout){
if(ShowCheckoutTimeOnCheckout||ShowVorbereitungTimeOnCheckout){echo "<br>";}
echo "Packen";
}
if(ShowDeliveryTimeOnCheckout){
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
echo "Liefern";
}
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
echo '</th>';
}
?>
<th scope="col">Status</th>
<?php
if(ShowShippingIcons){
echo "<th scope='col'>Shipping</th>";
}
?>
</tr>
</thead>
<tbody id="getCheckOutTableHolder"></tbody>
</table>
</div>
</div>
<div class="col-sm">
<h2 class="text-light">Check-In</h2>
<div class="tableFixHead" id="checkin-scroll">
<table class="table table-dark mb-0" id="checkin-table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Kunde</th>
<th scope="col">Event</th> <?php
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin ||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
echo '<th scope="col">';
}
if(ShowCheckInTimeOnCheckin){
echo "Dispo-Ende";
}
if(ShowNachbereitungTimeOnCheckin){
if(ShowCheckInTimeOnCheckin){echo "<br>";}
echo "Nachbereitung";
}
if(ShowRePackagingTimeOnCheckin){
if(ShowCheckInTimeOnCheckin||ShowNachbereitungTimeOnCheckin){echo "<br>";}
echo "Zurückpacken";
}
if(ShowReDeliveryTimeOnCheckin){
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin){echo "<br>";}
echo "Rückliefern";
}
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
echo '</th>';
}
?>
<th scope="col">Status</th>
<?php
if(ShowShippingIcons){
echo "<th scope='col'>Shipping</th>";
}
?>
</tr>
</thead>
<tbody id="getCheckInTableHolder"></tbody>
</table>
</div>
</div>
<div class="col-sm">
<h2 class="text-light">Aufgaben</h2>
<div class="tableFixHead" id="aufgaben-scroll">
<table class="table table-dark mb-0" id="aufgaben-table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Bearbeiter</th>
<th scope="col">Aufgabe</th>
<th scope="col">Zieldatum</th>
<th scope="col">Priorität</th>
</tr>
</thead>
<tbody id="AufgabenTableHolder"></tbody>
</table>
</div>
</div>
</div>
@@ -182,7 +359,6 @@ $Epi = new Epirent();
<!-- 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>

View File

@@ -1,9 +1,4 @@
<?php
error_reporting(E_ALL);
require('config.php');
require('EpiApi.php');
require('vendor/autoload.php');
@@ -14,143 +9,330 @@ $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">
<!-- 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]-->
<!-- Bootstrap -->
<link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css">
<script src="scripts/jquery-3.5.1.min.js"></script>
<link href="css/sticky-footer.css" rel="stylesheet">
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
<script type="text/javascript">
$(document).ready(function () {
refreshCheckOutTable();
refreshCheckInTable();
});
<script type="text/javascript">
// === Höhe der Scroll-Container bis Viewport-Ende anpassen ===
function sizeScrollContainers() {
$('.tableFixHead').each(function () {
const rect = this.getBoundingClientRect();
const bottomMargin = 16;
const available = window.innerHeight - rect.top - bottomMargin;
this.style.maxHeight = (available > 120 ? available : 120) + 'px';
});
}
function refreshCheckOutTable() {
$('#getCheckOutTableHolder').load('sources/getCheckOutTable.php', function () {
setTimeout(refreshCheckOutTable, 5000);
});
// === 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;
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 ===
// Klont den ersten <tbody> als __loopClone ans Tabellenende, wenn Inhalt > Sichtbereich.
function buildSeamlessLoop($table, $scroller) {
$table.find('tbody.__loopClone').remove();
const $main = $table.find('tbody').first();
const scEl = $scroller.get(0);
const st = ensureState(scEl);
if ($main.length === 0 || $main.children().length === 0) {
st.loopHeight = 0;
return;
}
const mainH = $main.get(0).offsetHeight;
const needScroll = mainH > scEl.clientHeight + 1;
if (!needScroll) {
st.loopHeight = 0;
return;
}
const $clone = $main.clone(false, false).addClass('__loopClone').attr('aria-hidden', 'true');
$table.append($clone);
st.loopHeight = mainH; // Höhe des Original-Inhalts als Loop-Länge
}
// === Auto-Scroll (nahtlos) ===
function startAutoScroll($scroller, speedPx = 1, stepMs = 40) {
const el = $scroller.get(0);
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;
// Nahtlos: sobald wir das Ende des Original-Blocks überschreiten, ziehen wir loopHeight ab
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);
const st = ensureState(el);
if (st.userActive) return;
if (st.loopHeight > 0) startAutoScroll($scroller, 1, 40);
else stopAutoScroll($scroller);
}
// === AJAX-Reload: relative Position innerhalb der Loop erhalten ===
// Wir merken die Position modulo loopHeight; nach dem Reload setzen wir proportional um.
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 () {
// Loop neu bewerten/aufbauen
buildSeamlessLoop($table, $scroller);
if (st.loopHeight > 0) {
// Neue relative Position setzen (proportional)
const newPos = Math.floor(posRatio * st.loopHeight);
if (Math.abs(scEl.scrollTop - newPos) > 1) {
// rAF für flüssiges Setzen außerhalb des load()-Layouts
requestAnimationFrame(() => { scEl.scrollTop = newPos; });
}
function refreshCheckInTable() {
$('#getCheckInTableHolder').load('sources/getCheckInTable.php', function () {
setTimeout(refreshCheckInTable, 5000);
});
// Auto-Scroll (wieder) starten
stopAutoScroll($scroller);
startAutoScroll($scroller, 1, 40);
} else {
// kein Overflow -> ganz oben und Auto-Scroll aus
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();
// Nach Layoutwechsel neu entscheiden (nur 2 Tabellen)
['#checkout', '#checkin'].forEach(prefix => {
const $scroller = $(`${prefix}-scroll`);
const $table = $(`${prefix}-table`);
buildSeamlessLoop($table, $scroller);
maybeStartAutoScroll($scroller);
});
});
// Guards pro Scroller (nur 2)
attachScrollGuards($('#checkout-scroll'));
attachScrollGuards($('#checkin-scroll'));
// Erstladen je Tabelle: laden -> Loop bauen -> ggf. Auto-Scroll -> zyklisch refreshen
function initOne(scrollerSel, tableSel, tbodySel, url, ms) {
const $scroller = $(scrollerSel);
const $table = $(tableSel);
const $target = $(tbodySel);
$target.load(url, function () {
buildSeamlessLoop($table, $scroller);
maybeStartAutoScroll($scroller);
setTimeout(() => smartLoad($scroller, $table, $target, url, ms), ms);
});
}
initOne('#checkout-scroll', '#checkout-table', '#getCheckOutTableHolder', 'sources/getCheckOutTable.php', 5000);
initOne('#checkin-scroll', '#checkin-table', '#getCheckInTableHolder', 'sources/getCheckInTable.php', 5000);
});
</script>
<style>
/* Scroll-Wrapper je Tabelle */
.tableFixHead {
overflow-y: auto;
/* Höhe wird per JS dynamisch gesetzt, damit genau bis zum Viewport-Ende gescrollt wird */
max-height: 60vh; /* Fallback */
border: 1px solid rgba(255,255,255,.1);
border-radius: .25rem;
}
/* Sticky Header */
.tableFixHead thead th {
position: sticky;
top: 0;
z-index: 2; /* über Body-Zellen */
}
/* Saubere Hintergrundfarbe für sticky Header (Bootstrap .table-dark) */
.table-dark thead th {
background-color: #212529; /* gleiche Farbe wie .table-dark header */
}
/* Optional: dünne Trennlinien */
.table-dark tbody tr + tr td {
border-top: 1px solid rgba(255,255,255,.08);
}
body { background-color: black; margin-bottom: 0; }
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- Check-Out -->
<div class="col-sm">
<h2 class="text-light">Check-Out</h2>
<div class="tableFixHead" id="checkout-scroll">
<table class="table table-dark mb-0" id="checkout-table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Kunde</th>
<th scope="col">Event</th>
<?php
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
echo '<th scope="col">';
}
if(ShowCheckoutTimeOnCheckout){
echo "Dispo-Start";
}
if(ShowVorbereitungTimeOnCheckout){
if(ShowCheckoutTimeOnCheckout){echo "<br>";}
echo "VB-Start";
}
if(ShowPackagingTimeOnCheckout){
if(ShowCheckoutTimeOnCheckout||ShowVorbereitungTimeOnCheckout){echo "<br>";}
echo "Packen";
}
if(ShowDeliveryTimeOnCheckout){
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
echo "Liefern";
}
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
echo '</th>';
}
?>
<th scope="col">Status</th>
<?php
if(ShowShippingIcons){
echo "<th scope='col'>Shipping</th>";
}
?>
</tr>
</thead>
<tbody id="getCheckOutTableHolder"></tbody>
</table>
</div>
</div>
<!-- Check-In -->
<div class="col-sm">
<h2 class="text-light">Check-In</h2>
<div class="tableFixHead" id="checkin-scroll">
<table class="table table-dark mb-0" id="checkin-table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Kunde</th>
<th scope="col">Event</th>
<?php
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin ||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
echo '<th scope="col">';
}
</script>
</head>
<body style="background-color: black;">
<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">Dispo-Start<br><i>VB-Start</i></th>
<?php
if(!HideCheckInTimeOnCheckout){
echo "<th scope='col'>Dispo-Ende<br><i>VB-Ende</i></th>";
}
?>
<th scope="col">Status</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>";
}
?>
<th scope="col">Dispo-Ende<br><i>RP-Ende</i></th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody id="getCheckInTableHolder">
</tbody>
</table>
</div>
</div>
</header>
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);
}
?>

View File

@@ -1,8 +1,8 @@
# EPIWebview
**aktuellste stable Version:** 1.5.0
**Lizenz:** Creative Commons Attribution-NonCommercial-ShareAlike 4.0 (CC BY-NC-SA 4.0). Einsehbar unter: (LICENSE.MD)
**Kompatibilität:** Erweiterung für **Epirent** und **CrewBrain**
- **aktuellste stable Version:** 1.5.0
- **Lizenz:** Creative Commons Attribution-NonCommercial-ShareAlike 4.0 (CC BY-NC-SA 4.0). Einsehbar unter: (LICENSE.MD)
- **Kompatibilität:** Erweiterung für **Epirent** und **CrewBrain**
---
@@ -38,10 +38,5 @@ Die Anwendung ist speziell für den Einsatz in Lagerprozessen entwickelt.
git clone http://srvgitea01.vtm.zone:3000/epi/EpiWebview
## Changelog
### Änderungen in Version 1.5.0
**Neu:**
- Die Spalte **„CheckIn“** kann beim **Checkout** ausgeblendet werden.
- Die Spalte **„CheckOut“** kann beim **Checkin** ausgeblendet werden.
Verschoben in Releases (Git)
---

7
dist/index.php vendored
View File

@@ -11,6 +11,13 @@
<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>
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
<link href="/src/fa/css/fontawesome.css" rel="stylesheet" />
<link href="/src/fa/css/brands.css" rel="stylesheet" />
<link href="/src/fa/css/solid.css" rel="stylesheet" />
<link href="/src/fa/css/sharp-thin.css" rel="stylesheet" />
<link href="/src/fa/css/sharp-duotone-thin.css" rel="stylesheet" />
<script type="text/javascript">

View File

@@ -20,16 +20,53 @@ define('Enable_QR_Code_CrewBrainAufgaben', true);
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, der Zusätzlich zur DispoZeit beim Check In Angezeigt werden soll
define('Rückpacken_Zeitvariable', 'Rückpacken');
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".
/** -------------------- Row-Marking: Konfig Zusände --------------------
* 1 = $packingjob->date_start (Dispo Start)
* 2 = $VorbereitungsTimeDetail->date_start (Vorbereitung Start)
* 3 = $PackingNoteDetail->date_packing (Packen Zeit)
* 4 = $PackingNoteDetail->date_delivery (Delivery Zeit)
*/
define('CheckOutRowMarkSource', 4);
/** -------------------- Row-Marking: Konfig Zusände --------------------
* 1 = $packingjob->date_end (Dispo Ende)
* 2 = $NachbereitungssTimeDetail->date_start (Nachbereitung Start)
* 3 = $RePackagingTimeDetail->date_start (Rückpacken Zeit AUS AUFTRAG)
* 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('ShowCheckoutTimeOnCheckout', true);
define('ShowVorbereitungTimeOnCheckout', true);
define('ShowPackagingTimeOnCheckout', true);
define('ShowDeliveryTimeOnCheckout', true);
define('ShowTimesOnCheckout', true);
define('ShowCheckInTimeOnCheckin', true);
define('ShowNachbereitungTimeOnCheckin', true);
define('ShowRePackagingTimeOnCheckin', true);
define('ShowReDeliveryTimeOnCheckin', true);
define('ShowTimesOnCheckin', true);
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.
define('DHLContainsText', 'DHL'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als DHL erkannt wird.
define('LKWContainsText', 'LKW'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener LKW erkannt wird.
define('TransporterContainsText', 'Sprinter'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener Transporter erkannt wird.
define('PKWContainsText', 'PKW'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener PKW erkannt wird.
define('UseShippingStatus', true); //Nutzt den Packscheinstatus als Shipping Status. Hierdurch können die Icons unter verschiedenen Vorraussetzungen Grün werden, wenn der Versand organisiert wurde
define('ShippingOutOrganizedStatus', 'Hinversand OK'); //Packschein Status (EpirentDropdown) der Gematcht werden muss, dass der Hinversand Organisiert / Bestellt wurde
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)
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

View File

@@ -62,9 +62,13 @@ foreach ($data_output as $aufgabe) {
}
echo "<td>";
$i=0;
foreach($AufgabeDetail->Responsibles as $bearbeiter){
echo $bearbeiter->Name;
if($i>0){
echo ", ";
}
$i++;
}
echo "</td>";
echo "<td>" . $aufgabe->Titel . "</td>";

View File

@@ -10,133 +10,240 @@ 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];
$NachbereitungsTimeDetail;
// PackingNote Details
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
// Zeit-Slots aus dem Schedule
$NachbereitungsTimeDetail = null;
$RePackagingTimeDetail = null;
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
if ($scheduledetail->name == Rueckpacken_Zeitvariable) {
if ($scheduledetail->name == Nachbereitung_Zeitvariable) {
$NachbereitungsTimeDetail = $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);
}
$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) {
echo "<tr class='text-dark bg-warning'>";
} else if ($date < $today) {
echo "<tr class=' bg-danger'>";
} else {
echo "<tr>";
if ($scheduledetail->name == Rückpacken_Zeitvariable) {
$RePackagingTimeDetail = $scheduledetail;
}
}
// --- 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');
}
// Override-Flag wie bei Checkout: Dispo-Ende erzwingen
$source = CheckIn_UseDispoEndForRowMarking ? 1 : (int)CheckInRowMarkSource;
$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) {
echo "<td><small>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</small><br><i>" . date_format(new \DateTime($NachbereitungsTimeDetail->date_start), 'd.m.Y') . " " . getTimeFromSeconds($NachbereitungsTimeDetail->time_start) . "</i></td>";
} else {
echo "<td>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</td>";
// Zeitspalte öffnen, wenn mindestens eine Anzeige aktiv ist
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin) {
echo "<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><small>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</small><br><i>" . date_format(new \DateTime($NachbereitungsTimeDetail->date_end), 'd.m.Y') . " " . getTimeFromSeconds($NachbereitungsTimeDetail->time_end) . "</i></td>";
} else {
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 "</span></td>";
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>";
// 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 ($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 "</tr>";
}
echo "</tr>";
}
@@ -144,14 +251,9 @@ foreach ($data_output as $packingjob) {
}
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;
}
?>

View File

@@ -1,5 +1,6 @@
<?php
error_reporting(E_ALL);
require('../config.php');
require('../EpiApi.php');
@@ -19,20 +20,64 @@ $options = new QROptions([
'quietzoneSize' => 1
]);
$Epi = new Epirent();
const APP_TZ = 'Europe/Berlin';
$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) {
@@ -57,45 +102,54 @@ if (SortCheckOut == 2) {
foreach ($data_output as $packingjob) {
$PackingNoteDetail = null;
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) {
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];
$VorbereitungsTimeDetail;
// 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 = null;
foreach ($orderdetail_output->order_schedule as $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 {
$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>";
}
@@ -107,20 +161,38 @@ foreach ($data_output as $packingjob) {
}
echo "<td>" . $packingjob->contact->name . "</td>";
echo "<td>" . $packingjob->event . "</td>";
if ($VorbereitungsTimeDetail->date_start != null) {
echo "<td><small>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</small><br><i>" . date_format(new \DateTime($VorbereitungsTimeDetail->date_start), 'd.m.Y') . " " . getTimeFromSeconds($VorbereitungsTimeDetail->time_start) . "</i></td>";
} else {
echo "<td>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</td>";
}
if(!HideCheckInTimeOnCheckout){
if (($VorbereitungsTimeDetail->date_end != null)) {
echo "<td><small>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</small><br><i>" . date_format(new \DateTime($VorbereitungsTimeDetail->date_end), 'd.m.Y') . " " . getTimeFromSeconds($VorbereitungsTimeDetail->time_end) . "</i></td>";
} else {
echo "<td>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</td>";
}
}
echo "<td>";
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout) { 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>";
}
echo "<td>";
if ($packingjob->is_all_out == 0) {
echo "<span class='badge badge-success'>";
@@ -132,12 +204,66 @@ foreach ($data_output as $packingjob) {
}
echo ($packingjob->pieces_sum_total - abs($packingjob->is_all_out)) . "/" . $packingjob->pieces_sum_total;
echo "</span><td>";
echo "</span></td>";
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 ($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 "</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);

10663
src/fa/css/all.css Normal file

File diff suppressed because it is too large Load Diff

9
src/fa/css/all.min.css vendored Normal file

File diff suppressed because one or more lines are too long

2219
src/fa/css/brands.css Normal file

File diff suppressed because it is too large Load Diff

6
src/fa/css/brands.min.css vendored Normal file

File diff suppressed because one or more lines are too long

8361
src/fa/css/fontawesome.css vendored Normal file

File diff suppressed because it is too large Load Diff

8
src/fa/css/fontawesome.min.css vendored Normal file

File diff suppressed because one or more lines are too long

31
src/fa/css/regular.css Normal file
View File

@@ -0,0 +1,31 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
:root, :host {
--fa-family-classic: "Font Awesome 7 Free";
--fa-font-regular: normal 400 1em/1 var(--fa-family-classic);
/* deprecated: this older custom property will be removed next major release */
--fa-style-family-classic: var(--fa-family-classic);
}
@font-face {
font-family: "Font Awesome 7 Free";
font-style: normal;
font-weight: 400;
font-display: block;
src: url("../webfonts/fa-regular-400.woff2");
}
.far {
--fa-family: var(--fa-family-classic);
--fa-style: 400;
}
.fa-classic {
--fa-family: var(--fa-family-classic);
}
.fa-regular {
--fa-style: 400;
}

6
src/fa/css/regular.min.css vendored Normal file
View File

@@ -0,0 +1,6 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
:host,:root{--fa-family-classic:"Font Awesome 7 Free";--fa-font-regular:normal 400 1em/1 var(--fa-family-classic);--fa-style-family-classic:var(--fa-family-classic)}@font-face{font-family:"Font Awesome 7 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2)}.far{--fa-style:400}.fa-classic,.far{--fa-family:var(--fa-family-classic)}.fa-regular{--fa-style:400}

31
src/fa/css/solid.css Normal file
View File

@@ -0,0 +1,31 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
:root, :host {
--fa-family-classic: "Font Awesome 7 Free";
--fa-font-solid: normal 900 1em/1 var(--fa-family-classic);
/* deprecated: this older custom property will be removed next major release */
--fa-style-family-classic: var(--fa-family-classic);
}
@font-face {
font-family: "Font Awesome 7 Free";
font-style: normal;
font-weight: 900;
font-display: block;
src: url("../webfonts/fa-solid-900.woff2");
}
.fas {
--fa-family: var(--fa-family-classic);
--fa-style: 900;
}
.fa-classic {
--fa-family: var(--fa-family-classic);
}
.fa-solid {
--fa-style: 900;
}

6
src/fa/css/solid.min.css vendored Normal file
View File

@@ -0,0 +1,6 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
:host,:root{--fa-family-classic:"Font Awesome 7 Free";--fa-font-solid:normal 900 1em/1 var(--fa-family-classic);--fa-style-family-classic:var(--fa-family-classic)}@font-face{font-family:"Font Awesome 7 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2)}.fas{--fa-style:900}.fa-classic,.fas{--fa-family:var(--fa-family-classic)}.fa-solid{--fa-style:900}

556
src/fa/css/svg-with-js.css Normal file
View File

@@ -0,0 +1,556 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
:root, :host {
--fa-font-solid: normal 900 1em/1 "Font Awesome 7 Free";
--fa-font-regular: normal 400 1em/1 "Font Awesome 7 Free";
--fa-font-light: normal 300 1em/1 "Font Awesome 7 Pro";
--fa-font-thin: normal 100 1em/1 "Font Awesome 7 Pro";
--fa-font-duotone: normal 900 1em/1 "Font Awesome 7 Duotone";
--fa-font-duotone-regular: normal 400 1em/1 "Font Awesome 7 Duotone";
--fa-font-duotone-light: normal 300 1em/1 "Font Awesome 7 Duotone";
--fa-font-duotone-thin: normal 100 1em/1 "Font Awesome 7 Duotone";
--fa-font-brands: normal 400 1em/1 "Font Awesome 7 Brands";
--fa-font-sharp-solid: normal 900 1em/1 "Font Awesome 7 Sharp";
--fa-font-sharp-regular: normal 400 1em/1 "Font Awesome 7 Sharp";
--fa-font-sharp-light: normal 300 1em/1 "Font Awesome 7 Sharp";
--fa-font-sharp-thin: normal 100 1em/1 "Font Awesome 7 Sharp";
--fa-font-sharp-duotone-solid: normal 900 1em/1 "Font Awesome 7 Sharp Duotone";
--fa-font-sharp-duotone-regular: normal 400 1em/1 "Font Awesome 7 Sharp Duotone";
--fa-font-sharp-duotone-light: normal 300 1em/1 "Font Awesome 7 Sharp Duotone";
--fa-font-sharp-duotone-thin: normal 100 1em/1 "Font Awesome 7 Sharp Duotone";
--fa-font-slab-regular: normal 400 1em/1 "Font Awesome 7 Slab";
--fa-font-slab-press-regular: normal 400 1em/1 "Font Awesome 7 Slab Press";
--fa-font-whiteboard-semibold: normal 600 1em/1 "Font Awesome 7 Whiteboard";
--fa-font-thumbprint-light: normal 300 1em/1 "Font Awesome 7 Thumbprint";
--fa-font-notdog-solid: normal 900 1em/1 "Font Awesome 7 Notdog";
--fa-font-notdog-duo-solid: normal 900 1em/1 "Font Awesome 7 Notdog Duo";
--fa-font-etch-solid: normal 900 1em/1 "Font Awesome 7 Etch";
--fa-font-jelly-regular: normal 400 1em/1 "Font Awesome 7 Jelly";
--fa-font-jelly-fill-regular: normal 400 1em/1 "Font Awesome 7 Jelly Fill";
--fa-font-jelly-duo-regular: normal 400 1em/1 "Font Awesome 7 Jelly Duo";
--fa-font-chisel-regular: normal 400 1em/1 "Font Awesome 7 Chisel";
--fa-font-utility-semibold: normal 600 1em/1 "Font Awesome 7 Utility";
--fa-font-utility-duo-semibold: normal 600 1em/1 "Font Awesome 7 Utility Duo";
--fa-font-utility-fill-semibold: normal 600 1em/1 "Font Awesome 7 Utility Fill";
}
.svg-inline--fa {
box-sizing: content-box;
display: var(--fa-display, inline-block);
height: 1em;
overflow: visible;
vertical-align: -0.125em;
width: var(--fa-width, 1.25em);
}
.svg-inline--fa.fa-2xs {
vertical-align: 0.1em;
}
.svg-inline--fa.fa-xs {
vertical-align: 0em;
}
.svg-inline--fa.fa-sm {
vertical-align: -0.0714285714em;
}
.svg-inline--fa.fa-lg {
vertical-align: -0.2em;
}
.svg-inline--fa.fa-xl {
vertical-align: -0.25em;
}
.svg-inline--fa.fa-2xl {
vertical-align: -0.3125em;
}
.svg-inline--fa.fa-pull-left,
.svg-inline--fa .fa-pull-start {
float: inline-start;
margin-inline-end: var(--fa-pull-margin, 0.3em);
}
.svg-inline--fa.fa-pull-right,
.svg-inline--fa .fa-pull-end {
float: inline-end;
margin-inline-start: var(--fa-pull-margin, 0.3em);
}
.svg-inline--fa.fa-li {
width: var(--fa-li-width, 2em);
inset-inline-start: calc(-1 * var(--fa-li-width, 2em));
inset-block-start: 0.25em; /* syncing vertical alignment with Web Font rendering */
}
.fa-layers-counter, .fa-layers-text {
display: inline-block;
position: absolute;
text-align: center;
}
.fa-layers {
display: inline-block;
height: 1em;
position: relative;
text-align: center;
vertical-align: -0.125em;
width: var(--fa-width, 1.25em);
}
.fa-layers .svg-inline--fa {
inset: 0;
margin: auto;
position: absolute;
transform-origin: center center;
}
.fa-layers-text {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
transform-origin: center center;
}
.fa-layers-counter {
background-color: var(--fa-counter-background-color, #ff253a);
border-radius: var(--fa-counter-border-radius, 1em);
box-sizing: border-box;
color: var(--fa-inverse, #fff);
line-height: var(--fa-counter-line-height, 1);
max-width: var(--fa-counter-max-width, 5em);
min-width: var(--fa-counter-min-width, 1.5em);
overflow: hidden;
padding: var(--fa-counter-padding, 0.25em 0.5em);
right: var(--fa-right, 0);
text-overflow: ellipsis;
top: var(--fa-top, 0);
transform: scale(var(--fa-counter-scale, 0.25));
transform-origin: top right;
}
.fa-layers-bottom-right {
bottom: var(--fa-bottom, 0);
right: var(--fa-right, 0);
top: auto;
transform: scale(var(--fa-layers-scale, 0.25));
transform-origin: bottom right;
}
.fa-layers-bottom-left {
bottom: var(--fa-bottom, 0);
left: var(--fa-left, 0);
right: auto;
top: auto;
transform: scale(var(--fa-layers-scale, 0.25));
transform-origin: bottom left;
}
.fa-layers-top-right {
top: var(--fa-top, 0);
right: var(--fa-right, 0);
transform: scale(var(--fa-layers-scale, 0.25));
transform-origin: top right;
}
.fa-layers-top-left {
left: var(--fa-left, 0);
right: auto;
top: var(--fa-top, 0);
transform: scale(var(--fa-layers-scale, 0.25));
transform-origin: top left;
}
.fa-1x {
font-size: 1em;
}
.fa-2x {
font-size: 2em;
}
.fa-3x {
font-size: 3em;
}
.fa-4x {
font-size: 4em;
}
.fa-5x {
font-size: 5em;
}
.fa-6x {
font-size: 6em;
}
.fa-7x {
font-size: 7em;
}
.fa-8x {
font-size: 8em;
}
.fa-9x {
font-size: 9em;
}
.fa-10x {
font-size: 10em;
}
.fa-2xs {
font-size: calc(10 / 16 * 1em); /* converts a 10px size into an em-based value that's relative to the scale's 16px base */
line-height: calc(1 / 10 * 1em); /* sets the line-height of the icon back to that of it's parent */
vertical-align: calc((6 / 10 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
}
.fa-xs {
font-size: calc(12 / 16 * 1em); /* converts a 12px size into an em-based value that's relative to the scale's 16px base */
line-height: calc(1 / 12 * 1em); /* sets the line-height of the icon back to that of it's parent */
vertical-align: calc((6 / 12 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
}
.fa-sm {
font-size: calc(14 / 16 * 1em); /* converts a 14px size into an em-based value that's relative to the scale's 16px base */
line-height: calc(1 / 14 * 1em); /* sets the line-height of the icon back to that of it's parent */
vertical-align: calc((6 / 14 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
}
.fa-lg {
font-size: calc(20 / 16 * 1em); /* converts a 20px size into an em-based value that's relative to the scale's 16px base */
line-height: calc(1 / 20 * 1em); /* sets the line-height of the icon back to that of it's parent */
vertical-align: calc((6 / 20 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
}
.fa-xl {
font-size: calc(24 / 16 * 1em); /* converts a 24px size into an em-based value that's relative to the scale's 16px base */
line-height: calc(1 / 24 * 1em); /* sets the line-height of the icon back to that of it's parent */
vertical-align: calc((6 / 24 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
}
.fa-2xl {
font-size: calc(32 / 16 * 1em); /* converts a 32px size into an em-based value that's relative to the scale's 16px base */
line-height: calc(1 / 32 * 1em); /* sets the line-height of the icon back to that of it's parent */
vertical-align: calc((6 / 32 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
}
.fa-width-auto {
--fa-width: auto;
}
.fa-fw,
.fa-width-fixed {
--fa-width: 1.25em;
}
.fa-ul {
list-style-type: none;
margin-inline-start: var(--fa-li-margin, 2.5em);
padding-inline-start: 0;
}
.fa-ul > li {
position: relative;
}
.fa-li {
inset-inline-start: calc(-1 * var(--fa-li-width, 2em));
position: absolute;
text-align: center;
width: var(--fa-li-width, 2em);
line-height: inherit;
}
/* Heads Up: Bordered Icons will not be supported in the future!
- This feature will be deprecated in the next major release of Font Awesome (v8)!
- You may continue to use it in this version *v7), but it will not be supported in Font Awesome v8.
*/
/* Notes:
* --@{v.$css-prefix}-border-width = 1/16 by default (to render as ~1px based on a 16px default font-size)
* --@{v.$css-prefix}-border-padding =
** 3/16 for vertical padding (to give ~2px of vertical whitespace around an icon considering it's vertical alignment)
** 4/16 for horizontal padding (to give ~4px of horizontal whitespace around an icon)
*/
.fa-border {
border-color: var(--fa-border-color, #eee);
border-radius: var(--fa-border-radius, 0.1em);
border-style: var(--fa-border-style, solid);
border-width: var(--fa-border-width, 0.0625em);
box-sizing: var(--fa-border-box-sizing, content-box);
padding: var(--fa-border-padding, 0.1875em 0.25em);
}
.fa-pull-left,
.fa-pull-start {
float: inline-start;
margin-inline-end: var(--fa-pull-margin, 0.3em);
}
.fa-pull-right,
.fa-pull-end {
float: inline-end;
margin-inline-start: var(--fa-pull-margin, 0.3em);
}
.fa-beat {
animation-name: fa-beat;
animation-delay: var(--fa-animation-delay, 0s);
animation-direction: var(--fa-animation-direction, normal);
animation-duration: var(--fa-animation-duration, 1s);
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
animation-timing-function: var(--fa-animation-timing, ease-in-out);
}
.fa-bounce {
animation-name: fa-bounce;
animation-delay: var(--fa-animation-delay, 0s);
animation-direction: var(--fa-animation-direction, normal);
animation-duration: var(--fa-animation-duration, 1s);
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1));
}
.fa-fade {
animation-name: fa-fade;
animation-delay: var(--fa-animation-delay, 0s);
animation-direction: var(--fa-animation-direction, normal);
animation-duration: var(--fa-animation-duration, 1s);
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1));
}
.fa-beat-fade {
animation-name: fa-beat-fade;
animation-delay: var(--fa-animation-delay, 0s);
animation-direction: var(--fa-animation-direction, normal);
animation-duration: var(--fa-animation-duration, 1s);
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1));
}
.fa-flip {
animation-name: fa-flip;
animation-delay: var(--fa-animation-delay, 0s);
animation-direction: var(--fa-animation-direction, normal);
animation-duration: var(--fa-animation-duration, 1s);
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
animation-timing-function: var(--fa-animation-timing, ease-in-out);
}
.fa-shake {
animation-name: fa-shake;
animation-delay: var(--fa-animation-delay, 0s);
animation-direction: var(--fa-animation-direction, normal);
animation-duration: var(--fa-animation-duration, 1s);
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
animation-timing-function: var(--fa-animation-timing, linear);
}
.fa-spin {
animation-name: fa-spin;
animation-delay: var(--fa-animation-delay, 0s);
animation-direction: var(--fa-animation-direction, normal);
animation-duration: var(--fa-animation-duration, 2s);
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
animation-timing-function: var(--fa-animation-timing, linear);
}
.fa-spin-reverse {
--fa-animation-direction: reverse;
}
.fa-pulse,
.fa-spin-pulse {
animation-name: fa-spin;
animation-direction: var(--fa-animation-direction, normal);
animation-duration: var(--fa-animation-duration, 1s);
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
animation-timing-function: var(--fa-animation-timing, steps(8));
}
@media (prefers-reduced-motion: reduce) {
.fa-beat,
.fa-bounce,
.fa-fade,
.fa-beat-fade,
.fa-flip,
.fa-pulse,
.fa-shake,
.fa-spin,
.fa-spin-pulse {
animation: none !important;
transition: none !important;
}
}
@keyframes fa-beat {
0%, 90% {
transform: scale(1);
}
45% {
transform: scale(var(--fa-beat-scale, 1.25));
}
}
@keyframes fa-bounce {
0% {
transform: scale(1, 1) translateY(0);
}
10% {
transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0);
}
30% {
transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em));
}
50% {
transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0);
}
57% {
transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em));
}
64% {
transform: scale(1, 1) translateY(0);
}
100% {
transform: scale(1, 1) translateY(0);
}
}
@keyframes fa-fade {
50% {
opacity: var(--fa-fade-opacity, 0.4);
}
}
@keyframes fa-beat-fade {
0%, 100% {
opacity: var(--fa-beat-fade-opacity, 0.4);
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(var(--fa-beat-fade-scale, 1.125));
}
}
@keyframes fa-flip {
50% {
transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg));
}
}
@keyframes fa-shake {
0% {
transform: rotate(-15deg);
}
4% {
transform: rotate(15deg);
}
8%, 24% {
transform: rotate(-18deg);
}
12%, 28% {
transform: rotate(18deg);
}
16% {
transform: rotate(-22deg);
}
20% {
transform: rotate(22deg);
}
32% {
transform: rotate(-12deg);
}
36% {
transform: rotate(12deg);
}
40%, 100% {
transform: rotate(0deg);
}
}
@keyframes fa-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.fa-rotate-90 {
transform: rotate(90deg);
}
.fa-rotate-180 {
transform: rotate(180deg);
}
.fa-rotate-270 {
transform: rotate(270deg);
}
.fa-flip-horizontal {
transform: scale(-1, 1);
}
.fa-flip-vertical {
transform: scale(1, -1);
}
.fa-flip-both,
.fa-flip-horizontal.fa-flip-vertical {
transform: scale(-1, -1);
}
.fa-rotate-by {
transform: rotate(var(--fa-rotate-angle, 0));
}
.svg-inline--fa .fa-primary {
fill: var(--fa-primary-color, currentColor);
opacity: var(--fa-primary-opacity, 1);
}
.svg-inline--fa .fa-secondary {
fill: var(--fa-secondary-color, currentColor);
opacity: var(--fa-secondary-opacity, 0.4);
}
.svg-inline--fa.fa-swap-opacity .fa-primary {
opacity: var(--fa-secondary-opacity, 0.4);
}
.svg-inline--fa.fa-swap-opacity .fa-secondary {
opacity: var(--fa-primary-opacity, 1);
}
.svg-inline--fa mask .fa-primary,
.svg-inline--fa mask .fa-secondary {
fill: black;
}
.svg-inline--fa.fa-inverse {
fill: var(--fa-inverse, #fff);
}
.fa-stack {
display: inline-block;
height: 2em;
line-height: 2em;
position: relative;
vertical-align: middle;
width: 2.5em;
}
.fa-inverse {
color: var(--fa-inverse, #fff);
}
.svg-inline--fa.fa-stack-1x {
--fa-width: 1.25em;
height: 1em;
width: var(--fa-width);
}
.svg-inline--fa.fa-stack-2x {
--fa-width: 2.5em;
height: 2em;
width: var(--fa-width);
}
.fa-stack-1x,
.fa-stack-2x {
inset: 0;
margin: auto;
position: absolute;
z-index: var(--fa-stack-z-index, auto);
}

6
src/fa/css/svg-with-js.min.css vendored Normal file

File diff suppressed because one or more lines are too long

182
src/fa/css/svg.css Normal file
View File

@@ -0,0 +1,182 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
:root, :host {
--fa-font-solid: normal 900 1em/1 "Font Awesome 7 Free";
--fa-font-regular: normal 400 1em/1 "Font Awesome 7 Free";
--fa-font-light: normal 300 1em/1 "Font Awesome 7 Pro";
--fa-font-thin: normal 100 1em/1 "Font Awesome 7 Pro";
--fa-font-duotone: normal 900 1em/1 "Font Awesome 7 Duotone";
--fa-font-duotone-regular: normal 400 1em/1 "Font Awesome 7 Duotone";
--fa-font-duotone-light: normal 300 1em/1 "Font Awesome 7 Duotone";
--fa-font-duotone-thin: normal 100 1em/1 "Font Awesome 7 Duotone";
--fa-font-brands: normal 400 1em/1 "Font Awesome 7 Brands";
--fa-font-sharp-solid: normal 900 1em/1 "Font Awesome 7 Sharp";
--fa-font-sharp-regular: normal 400 1em/1 "Font Awesome 7 Sharp";
--fa-font-sharp-light: normal 300 1em/1 "Font Awesome 7 Sharp";
--fa-font-sharp-thin: normal 100 1em/1 "Font Awesome 7 Sharp";
--fa-font-sharp-duotone-solid: normal 900 1em/1 "Font Awesome 7 Sharp Duotone";
--fa-font-sharp-duotone-regular: normal 400 1em/1 "Font Awesome 7 Sharp Duotone";
--fa-font-sharp-duotone-light: normal 300 1em/1 "Font Awesome 7 Sharp Duotone";
--fa-font-sharp-duotone-thin: normal 100 1em/1 "Font Awesome 7 Sharp Duotone";
--fa-font-slab-regular: normal 400 1em/1 "Font Awesome 7 Slab";
--fa-font-slab-press-regular: normal 400 1em/1 "Font Awesome 7 Slab Press";
--fa-font-whiteboard-semibold: normal 600 1em/1 "Font Awesome 7 Whiteboard";
--fa-font-thumbprint-light: normal 300 1em/1 "Font Awesome 7 Thumbprint";
--fa-font-notdog-solid: normal 900 1em/1 "Font Awesome 7 Notdog";
--fa-font-notdog-duo-solid: normal 900 1em/1 "Font Awesome 7 Notdog Duo";
--fa-font-etch-solid: normal 900 1em/1 "Font Awesome 7 Etch";
--fa-font-jelly-regular: normal 400 1em/1 "Font Awesome 7 Jelly";
--fa-font-jelly-fill-regular: normal 400 1em/1 "Font Awesome 7 Jelly Fill";
--fa-font-jelly-duo-regular: normal 400 1em/1 "Font Awesome 7 Jelly Duo";
--fa-font-chisel-regular: normal 400 1em/1 "Font Awesome 7 Chisel";
--fa-font-utility-semibold: normal 600 1em/1 "Font Awesome 7 Utility";
--fa-font-utility-duo-semibold: normal 600 1em/1 "Font Awesome 7 Utility Duo";
--fa-font-utility-fill-semibold: normal 600 1em/1 "Font Awesome 7 Utility Fill";
}
.svg-inline--fa {
box-sizing: content-box;
display: var(--fa-display, inline-block);
height: 1em;
overflow: visible;
vertical-align: -0.125em;
width: var(--fa-width, 1.25em);
}
.svg-inline--fa.fa-2xs {
vertical-align: 0.1em;
}
.svg-inline--fa.fa-xs {
vertical-align: 0em;
}
.svg-inline--fa.fa-sm {
vertical-align: -0.0714285714em;
}
.svg-inline--fa.fa-lg {
vertical-align: -0.2em;
}
.svg-inline--fa.fa-xl {
vertical-align: -0.25em;
}
.svg-inline--fa.fa-2xl {
vertical-align: -0.3125em;
}
.svg-inline--fa.fa-li {
width: var(--fa-li-width, 2em);
inset-inline-start: calc(-1 * var(--fa-li-width, 2em));
inset-block-start: 0.25em; /* syncing vertical alignment with Web Font rendering */
}
.fa-layers-counter, .fa-layers-text {
display: inline-block;
position: absolute;
text-align: center;
}
.fa-layers {
display: inline-block;
height: 1em;
position: relative;
text-align: center;
vertical-align: -0.125em;
width: var(--fa-width, 1.25em);
}
.fa-layers .svg-inline--fa {
inset: 0;
margin: auto;
position: absolute;
transform-origin: center center;
}
.fa-layers-text {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
transform-origin: center center;
}
.fa-layers-counter {
background-color: var(--fa-counter-background-color, #ff253a);
border-radius: var(--fa-counter-border-radius, 1em);
box-sizing: border-box;
color: var(--fa-inverse, #fff);
line-height: var(--fa-counter-line-height, 1);
max-width: var(--fa-counter-max-width, 5em);
min-width: var(--fa-counter-min-width, 1.5em);
overflow: hidden;
padding: var(--fa-counter-padding, 0.25em 0.5em);
right: var(--fa-right, 0);
text-overflow: ellipsis;
top: var(--fa-top, 0);
transform: scale(var(--fa-counter-scale, 0.25));
transform-origin: top right;
}
.fa-layers-bottom-right {
bottom: var(--fa-bottom, 0);
right: var(--fa-right, 0);
top: auto;
transform: scale(var(--fa-layers-scale, 0.25));
transform-origin: bottom right;
}
.fa-layers-bottom-left {
bottom: var(--fa-bottom, 0);
left: var(--fa-left, 0);
right: auto;
top: auto;
transform: scale(var(--fa-layers-scale, 0.25));
transform-origin: bottom left;
}
.fa-layers-top-right {
top: var(--fa-top, 0);
right: var(--fa-right, 0);
transform: scale(var(--fa-layers-scale, 0.25));
transform-origin: top right;
}
.fa-layers-top-left {
left: var(--fa-left, 0);
right: auto;
top: var(--fa-top, 0);
transform: scale(var(--fa-layers-scale, 0.25));
transform-origin: top left;
}
.svg-inline--fa .fa-primary {
fill: var(--fa-primary-color, currentColor);
opacity: var(--fa-primary-opacity, 1);
}
.svg-inline--fa .fa-secondary {
fill: var(--fa-secondary-color, currentColor);
opacity: var(--fa-secondary-opacity, 0.4);
}
.svg-inline--fa.fa-swap-opacity .fa-primary {
opacity: var(--fa-secondary-opacity, 0.4);
}
.svg-inline--fa.fa-swap-opacity .fa-secondary {
opacity: var(--fa-primary-opacity, 1);
}
.svg-inline--fa mask .fa-primary,
.svg-inline--fa mask .fa-secondary {
fill: black;
}
.svg-inline--fa.fa-inverse {
fill: var(--fa-inverse, #fff);
}
.fa-stack-1x,
.fa-stack-2x {
inset: 0;
margin: auto;
position: absolute;
z-index: var(--fa-stack-z-index, auto);
}

6
src/fa/css/svg.min.css vendored Normal file
View File

@@ -0,0 +1,6 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
:host,:root{--fa-font-solid:normal 900 1em/1 "Font Awesome 7 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 7 Free";--fa-font-light:normal 300 1em/1 "Font Awesome 7 Pro";--fa-font-thin:normal 100 1em/1 "Font Awesome 7 Pro";--fa-font-duotone:normal 900 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-regular:normal 400 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-light:normal 300 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-thin:normal 100 1em/1 "Font Awesome 7 Duotone";--fa-font-brands:normal 400 1em/1 "Font Awesome 7 Brands";--fa-font-sharp-solid:normal 900 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-regular:normal 400 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-light:normal 300 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-thin:normal 100 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-duotone-solid:normal 900 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-regular:normal 400 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-light:normal 300 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-thin:normal 100 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-slab-regular:normal 400 1em/1 "Font Awesome 7 Slab";--fa-font-slab-press-regular:normal 400 1em/1 "Font Awesome 7 Slab Press";--fa-font-whiteboard-semibold:normal 600 1em/1 "Font Awesome 7 Whiteboard";--fa-font-thumbprint-light:normal 300 1em/1 "Font Awesome 7 Thumbprint";--fa-font-notdog-solid:normal 900 1em/1 "Font Awesome 7 Notdog";--fa-font-notdog-duo-solid:normal 900 1em/1 "Font Awesome 7 Notdog Duo";--fa-font-etch-solid:normal 900 1em/1 "Font Awesome 7 Etch";--fa-font-jelly-regular:normal 400 1em/1 "Font Awesome 7 Jelly";--fa-font-jelly-fill-regular:normal 400 1em/1 "Font Awesome 7 Jelly Fill";--fa-font-jelly-duo-regular:normal 400 1em/1 "Font Awesome 7 Jelly Duo";--fa-font-chisel-regular:normal 400 1em/1 "Font Awesome 7 Chisel";--fa-font-utility-semibold:normal 600 1em/1 "Font Awesome 7 Utility";--fa-font-utility-duo-semibold:normal 600 1em/1 "Font Awesome 7 Utility Duo";--fa-font-utility-fill-semibold:normal 600 1em/1 "Font Awesome 7 Utility Fill"}.svg-inline--fa{box-sizing:content-box;display:var(--fa-display,inline-block);height:1em;overflow:visible;vertical-align:-.125em;width:var(--fa-width,1.25em)}.svg-inline--fa.fa-2xs{vertical-align:.1em}.svg-inline--fa.fa-xs{vertical-align:0}.svg-inline--fa.fa-sm{vertical-align:-.0714285714em}.svg-inline--fa.fa-lg{vertical-align:-.2em}.svg-inline--fa.fa-xl{vertical-align:-.25em}.svg-inline--fa.fa-2xl{vertical-align:-.3125em}.svg-inline--fa.fa-li{width:var(--fa-li-width,2em);inset-inline-start:calc(var(--fa-li-width, 2em)*-1);inset-block-start:.25em}.fa-layers-counter,.fa-layers-text{display:inline-block;position:absolute;text-align:center}.fa-layers{display:inline-block;height:1em;position:relative;text-align:center;vertical-align:-.125em;width:var(--fa-width,1.25em)}.fa-layers .svg-inline--fa{inset:0;margin:auto;position:absolute;transform-origin:center center}.fa-layers-text{left:50%;top:50%;transform:translate(-50%,-50%);transform-origin:center center}.fa-layers-counter{background-color:var(--fa-counter-background-color,#ff253a);border-radius:var(--fa-counter-border-radius,1em);box-sizing:border-box;color:var(--fa-inverse,#fff);line-height:var(--fa-counter-line-height,1);max-width:var(--fa-counter-max-width,5em);min-width:var(--fa-counter-min-width,1.5em);overflow:hidden;padding:var(--fa-counter-padding,.25em .5em);right:var(--fa-right,0);text-overflow:ellipsis;top:var(--fa-top,0);transform:scale(var(--fa-counter-scale,.25));transform-origin:top right}.fa-layers-bottom-right{bottom:var(--fa-bottom,0);right:var(--fa-right,0);top:auto;transform:scale(var(--fa-layers-scale,.25));transform-origin:bottom right}.fa-layers-bottom-left{bottom:var(--fa-bottom,0);left:var(--fa-left,0);right:auto;top:auto;transform:scale(var(--fa-layers-scale,.25));transform-origin:bottom left}.fa-layers-top-right{top:var(--fa-top,0);right:var(--fa-right,0);transform:scale(var(--fa-layers-scale,.25));transform-origin:top right}.fa-layers-top-left{left:var(--fa-left,0);right:auto;top:var(--fa-top,0);transform:scale(var(--fa-layers-scale,.25));transform-origin:top left}.svg-inline--fa .fa-primary{fill:var(--fa-primary-color,currentColor);opacity:var(--fa-primary-opacity,1)}.svg-inline--fa .fa-secondary{fill:var(--fa-secondary-color,currentColor)}.svg-inline--fa .fa-secondary,.svg-inline--fa.fa-swap-opacity .fa-primary{opacity:var(--fa-secondary-opacity,.4)}.svg-inline--fa.fa-swap-opacity .fa-secondary{opacity:var(--fa-primary-opacity,1)}.svg-inline--fa mask .fa-primary,.svg-inline--fa mask .fa-secondary{fill:#000}.svg-inline--fa.fa-inverse{fill:var(--fa-inverse,#fff)}.fa-stack-1x,.fa-stack-2x{inset:0;margin:auto;position:absolute;z-index:var(--fa-stack-z-index,auto)}

View File

@@ -0,0 +1,27 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
@font-face {
font-family: "FontAwesome";
font-display: block;
src: url("../webfonts/fa-solid-900.woff2") format("woff2");
}
@font-face {
font-family: "FontAwesome";
font-display: block;
src: url("../webfonts/fa-brands-400.woff2") format("woff2");
}
@font-face {
font-family: "FontAwesome";
font-display: block;
src: url("../webfonts/fa-regular-400.woff2") format("woff2");
unicode-range: U+F003, U+F006, U+F014, U+F016-F017, U+F01A-F01B, U+F01D, U+F022, U+F03E, U+F044, U+F046, U+F05C-F05D, U+F06E, U+F070, U+F087-F088, U+F08A, U+F094, U+F096-F097, U+F09D, U+F0A0, U+F0A2, U+F0A4-F0A7, U+F0C5, U+F0C7, U+F0E5-F0E6, U+F0EB, U+F0F6-F0F8, U+F10C, U+F114-F115, U+F118-F11A, U+F11C-F11D, U+F133, U+F147, U+F14E, U+F150-F152, U+F185-F186, U+F18E, U+F190-F192, U+F196, U+F1C1-F1C9, U+F1D9, U+F1DB, U+F1E3, U+F1EA, U+F1F7, U+F1F9, U+F20A, U+F247-F248, U+F24A, U+F24D, U+F255-F25B, U+F25D, U+F271-F274, U+F278, U+F27B, U+F28C, U+F28E, U+F29C, U+F2B5, U+F2B7, U+F2BA, U+F2BC, U+F2BE, U+F2C0-F2C1, U+F2C3, U+F2D0, U+F2D2, U+F2D4, U+F2DC;
}
@font-face {
font-family: "FontAwesome";
font-display: block;
src: url("../webfonts/fa-v4compatibility.woff2") format("woff2");
unicode-range: U+F041, U+F047, U+F065-F066, U+F07D-F07E, U+F080, U+F08B, U+F08E, U+F090, U+F09A, U+F0AC, U+F0AE, U+F0B2, U+F0D0, U+F0D6, U+F0E4, U+F0EC, U+F10A-F10B, U+F123, U+F13E, U+F148-F149, U+F14C, U+F156, U+F15E, U+F160-F161, U+F163, U+F175-F178, U+F195, U+F1F8, U+F219, U+F27A;
}

6
src/fa/css/v4-font-face.min.css vendored Normal file
View File

@@ -0,0 +1,6 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a}

2818
src/fa/css/v4-shims.css Normal file

File diff suppressed because it is too large Load Diff

6
src/fa/css/v4-shims.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
@font-face {
font-family: "Font Awesome 5 Brands";
font-display: block;
font-weight: 400;
src: url("../webfonts/fa-brands-400.woff2") format("woff2");
}
@font-face {
font-family: "Font Awesome 5 Free";
font-display: block;
font-weight: 900;
src: url("../webfonts/fa-solid-900.woff2") format("woff2");
}
@font-face {
font-family: "Font Awesome 5 Free";
font-display: block;
font-weight: 400;
src: url("../webfonts/fa-regular-400.woff2") format("woff2");
}

6
src/fa/css/v5-font-face.min.css vendored Normal file
View File

@@ -0,0 +1,6 @@
/*!
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2025 Fonticons, Inc.
*/
@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2")}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.