First version, for githup; UNSTABLE, DO NOT USE!

This commit is contained in:
Fabio Herzig
2026-04-12 21:25:44 +02:00
commit a51fd9dbeb
423 changed files with 58560 additions and 0 deletions

View File

@@ -0,0 +1,749 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
// Show all errors except deprecation notices (these come from vendor libraries
// that aren't yet typed for newer PHP versions). Long-term fix: update
// dependencies to versions compatible with your PHP runtime.
error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$logintype = 'wk_leitung';
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
require $baseDir . '/../scripts/login/login.php';
$logintype = '';
} else {
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo 'Critical DB Error.';
exit;
}
$allGeraete = db_select($mysqli, $tableGeraete, "name, start_index", "", [], "start_index ASC");
$allAbt = db_select($mysqli, $tableAbt, "name, id", "", [], "name ASC");
// $allGeraete[] = ['name' => 'null', 'id' => 'null'];
// $allAbt[] = ['name' => 'null', 'id' => 'null'];
$stmt = $mysqli->prepare("SELECT
t.id,
t.name,
t.vorname,
t.programm,
t.verein,
GROUP_CONCAT(a.name SEPARATOR ', ') AS abteilung,
GROUP_CONCAT(g.name SEPARATOR ', ') AS geraet
FROM $tableTurnerinnen t
LEFT JOIN $tableTurnerinnenAbt ta ON ta.turnerin_id = t.id
LEFT JOIN $tableAbt a ON a.id = ta.abteilung_id
LEFT JOIN $tableGeraete g ON g.id = ta.geraet_id
GROUP BY t.id
ORDER BY t.id DESC;
");
$stmt->execute();
$result = $stmt->get_result();
$allTurnerinnen = $result->fetch_all(MYSQLI_ASSOC);
$grouped = [];
foreach ($allTurnerinnen as $entry) {
if ($entry['abteilung'] == '') {
$grouped['null']['null'][] = $entry;
} else {
$grouped[$entry['abteilung']][$entry['geraet']][] = $entry;
}
}
$noAbt = $grouped;
foreach ($allAbt as $abt) {
$abtName = $abt['name'];
foreach ($allGeraete as $geraet) {
$geraetName = $geraet['name'];
$noAbt[$abtName][$geraetName] = [];
}
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Riegeneinteilung</title>
<link rel="icon" type="png" href="/intern/img/icon.png">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/intern/css/sidebar.css">
<link rel="stylesheet" href="/intern/css/riegeneinteilung.css">
<link rel="icon" type="png" href="/intern/img/icon.png">
<link href="/files/fonts/fonts.css" rel="stylesheet">
<script src="/intern/js/custom-msg-display.js"></script>
</head>
<body>
<?php
$currentPage = 'riegeneinteilung';
require $baseDir . '/intern/scripts/sidebar/sidebar.php';
?>
<?php sidebarRender('modal'); ?>
<div class="internMenuDiv">
<div class="innerInternMenuDiv">
<div class="header-text-conatiner">
<button id="autoEinteilung">
Automatische Riegeneinteilung
</button>
<button id="groupByVereinProgramm">
Group by Verein & Programm
</button>
<button id="ungroup">
Reset grouping
</button>
<label>Anzahl Abteilungen</label>
<input type="number" min="1" max="<?= count($allTurnerinnen) ?>" step="1" id="anzAbt" value="<?= count($allAbt) ?>">
</div>
<?php
$arrayRest = [];
foreach ($noAbt as $level1) {
foreach ($level1 as $level2) {
foreach ($level2 as $entry) {
$arrayRest[] = $entry;
}
}
}
$count = count($arrayRest);
echo '<div class="invalidAbtDiv"><table class="geraet-table"
data-abteilung="0">';
echo '<thead>
<tr>
<th>
Keine Valide Abt
<i><span>(' . $count . ')</span></i>
</th>
</tr>
</thead>';
echo '<tbody>';
foreach ($arrayRest as $entry) {
echo '<tr class="turnerin-row"
data-id="' . (int)$entry['id'] . '"
data-verein="' . htmlspecialchars($entry['verein']) . '"
data-programm="' . htmlspecialchars($entry['programm']) . '">
<td>' .
htmlspecialchars(
$entry['vorname'] . ' ' .
$entry['name'] . ' - ' .
$entry['programm'] . ' - ' .
$entry['verein']
) .
'</td>
</tr>';
}
echo '</tbody>';
echo '</table></div>';
?>
</div>
</div>
<section class="bgSection">
<div class="headerDivKampfrichter">
<h2 class="heading-pannel">Riegeneinteilungspanel</h2>
<div class="menuWrapper">
<div class="wk-leitungBurgerMenuDiv">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
</svg>
</div>
<?php sidebarRender('button'); ?>
</div>
<script>
const menuburger = document.querySelector('.wk-leitungBurgerMenuDiv');
const menudiv = document.querySelector('.internMenuDiv');
const content = document.querySelector('.bgSection');
const storageKey = "wk-leitungInternMenuOpen";
const isOpen = localStorage.getItem(storageKey) === "true";
document.addEventListener('keydown', function (e) {
// Mac = Option, Windows/Linux = Alt
const isOptionOrAlt = e.altKey || e.metaKey;
if (e.key.toLowerCase() === 'm') {
menuburger.classList.add("menuTransition");
menudiv.classList.add("menuTransition");
content.classList.add("menuTransition");
menuburger.classList.toggle("open");
menudiv.classList.toggle("open");
content.classList.toggle("open");
const isOpenEl =
menudiv.classList.contains("open") &&
menuburger.classList.contains("open");
localStorage.setItem(storageKey, isOpenEl);
}
});
if (isOpen) {
menuburger.classList.add("open");
menudiv.classList.add("open");
content.classList.add("open");
}
/*menubg.addEventListener("click", function () {
menuburger.classList.add("menuTransition");
menudiv.classList.add("menuTransition");
menubg.classList.add("menuTransition");
content.classList.add("menuTransition");
menuburger.classList.remove("open");
menudiv.classList.remove("open");
menubg.classList.remove("open");
content.classList.remove("open");
localStorage.setItem(storageKey, false);
});*/
menuburger.addEventListener("click", function () {
menuburger.classList.add("menuTransition");
menudiv.classList.add("menuTransition");
content.classList.add("menuTransition");
menuburger.classList.toggle("open");
menudiv.classList.toggle("open");
content.classList.toggle("open");
const isOpenEl =
menudiv.classList.contains("open") &&
menuburger.classList.contains("open");
localStorage.setItem(storageKey, isOpenEl);
});
</script>
</div>
<div class="allTurnerinenDiv">
<?php
//print_r($grouped);
foreach ($allAbt as $abt) {
$abtName = $abt['name'];
echo '<div style="display:flex; flex-direction:column;">';
echo '<div class="headerAbt"><h2>Abteilung ' . htmlspecialchars($abtName) . '</h2>
<button class="deleteProgramm" data-abt="'.(int)$abtName.'">
<!-- License: MIT. Made by xivapi: https://github.com/xivapi/classjob-icons -->
<svg
width="30px"
height="30px"
viewBox="0 0 1000 1000"
xmlns="http://www.w3.org/2000/svg"
transform="matrix(-1,0,0,1,0,0)"
>
<!-- BIN BODY -->
<path
fill="currentColor"
d="
M767 336H233q-12 0-21 9t-9 21l38 505
q1 13 12 21.5t30 8.5h434
q18 0 29-8.5t13-21.5l38-505
q0-12-9-21t-21-9z
M344 841q-10 0-18-9t-8-21l-26-386
q0-12 9-20.5t21-8.5 21 8.5 9 20.5
l18 386q0 12-7.5 21t-18.5 9z
M526 810q0 13-7.5 22t-18.5 9
-18.5-9-7.5-22l-4-385
q0-12 9-20.5t21-8.5 21 8.5 9 20.5z
M682 811q0 12-8 21t-18 9
q-11 0-18.5-9t-7.5-21l18-386
q0-12 9-20.5t21-8.5 21 8.5 9 20.5z
"
/>
<!-- BIN LID -->
<path
id="bin-lid"
fill="currentColor"
d="
M783 206l-179-30q-12-2-15-15l-8-33
q-4-20-14-26-6-3-22-3h-90
q-16 0-23 3-10 6-13 26l-8 33
q-2 13-15 15l-179 30
q-19 3-31.5 14.5T173 249v28
q0 9 6.5 15t15.5 6h610
q9 0 15.5-6t6.5-15v-28
q0-17-12.5-28.5T783 206z
"
/>
</svg>
</button></div>';
echo '<div style="display:flex; flex-direction:row; gap:16px; align-items:flex-start; margin-top:24px; margin-bottom:8px;">';
foreach ($allGeraete as $geraet) {
$geraetName = $geraet['name'];
// Safely resolve entries
$entries = $grouped[$abtName][$geraetName] ?? [];
$count = count($entries);
echo '<table class="geraet-table"
data-abteilung="' . htmlspecialchars($abtName) . '"
data-geraet="' . htmlspecialchars($geraetName) . '">';
echo '<thead>
<tr>
<th>
Gerät: ' . htmlspecialchars($geraetName) . '
<i><span>(' . $count . ')</span></i>
</th>
</tr>
</thead>';
echo '<tbody>';
foreach ($entries as $entry) {
echo '<tr class="turnerin-row"
data-id="' . (int)$entry['id'] . '"
data-verein="' . htmlspecialchars($entry['verein']) . '"
data-programm="' . htmlspecialchars($entry['programm']) . '">
<td>
<b>' . htmlspecialchars($entry['verein']) . '</b>,<br>' .
htmlspecialchars($entry['vorname']) . ' ' .
htmlspecialchars($entry['name']) . ', ' .
htmlspecialchars($entry['programm']) .
'</td>
</tr>';
}
$grouped[$abtName][$geraetName] = [];
echo '</tbody>';
echo '</table>';
}
echo '</div></div>';
}
?>
</div>
<script src="/intern/js/jquery/jquery-3.7.1.min.js"></script>
<script src="/intern/js/jquery/jquery-ui.min.js"></script>
<script src="/intern/js/jquery/jquery.ui.touch-punch.min.js"></script>
<link rel="stylesheet" href="/intern/js/jquery/jquery-ui.css">
<script>
$(function () {
function updateCounters() {
$(".geraet-table").each(function () {
const count = $(this).find("tr.turnerin-row").length;
$(this).find("thead span").text(`(${count})`);
});
}
$(".turnerin-row").each(function () {
$(this).data("original-table", $(this).closest(".geraet-table"));
});
function updateCounters() {
$(".geraet-table").each(function () {
const count = $(this).find(".turnerin-row").length;
$(this).find("thead span").text(`(${count})`);
});
}
const programmColorMap = {};
let nextColorIndex = 1;
function getColorForProgramm(programm) {
if (!programmColorMap[programm]) {
programmColorMap[programm] = colors[nextColorIndex % Object.keys(colors).length];
nextColorIndex++;
}
return programmColorMap[programm];
}
$(".geraet-table tbody").sortable({
connectWith: ".geraet-table tbody",
placeholder: "drop-placeholder",
helper: "clone",
start: function (e, ui) {
ui.item.addClass("dragging");
ui.item.data("sourceTbody", ui.item.closest(".geraet-table tbody"));
},
stop: function (e, ui) {
const item = ui.item;
const oldTable = item.data("sourceTbody");
const newTable = item.closest(".geraet-table");
const newAbt = newTable.data("abteilung");
const newGeraet = newTable.data("geraet");
// Remove placeholder from target table
newTable.find(".empty-drop-row").remove();
// Add placeholder to source table if empty
const sourceTbody = ui.sender || item.data("original-tbody");
if (
sourceTbody &&
sourceTbody.find(".turnerin-row").length === 0 &&
sourceTbody.find(".group-row").length === 0
) {
sourceTbody.append('<tr class="empty-drop-row"><td>&nbsp;</td></tr>');
}
item.removeClass("dragging");
// === AJAX UPDATE LOGIC ===
// GROUPED DROP
if (item.hasClass("group-row")) {
item.find(".turnerin-row").each(function () {
const turnerinId = $(this).data("id");
$.ajax({
url: "/intern/scripts/riegeneinteilung/ajax_update_turnerin.php",
method: "POST",
data: {
turnerin_id: turnerinId,
abteilung: newAbt,
geraet: newGeraet
}
});
});
}
// SINGLE ROW DROP
else {
const turnerinId = item.data("id");
$.ajax({
url: "/intern/scripts/riegeneinteilung/ajax_update_turnerin.php",
method: "POST",
data: {
turnerin_id: turnerinId,
abteilung: newAbt,
geraet: newGeraet
}
});
}
const payload = {
new: newIndexes(newTable),
old: newTable.is(oldTable) ? [] : newIndexes(oldTable)
};
$.ajax({
url: "/intern/scripts/riegeneinteilung/ajax_update_start_indexes.php",
method: "POST",
contentType: "application/json",
data: JSON.stringify(payload),
success: function () {
console.log("Order saved");
},
error: function () {
alert("Error saving order");
}
});
// Recalculate counters AFTER DOM is settled
updateCounters();
},
receive: function (e, ui) {
ui.item.data("original-tbody", $(this));
}
}).disableSelection();
function newIndexes(table) {
const order = [];
table.find(".turnerin-row").each(function (index) {
order.push({
id: $(this).data("id"),
order_index: index + 1
});
});
return order;
}
$(".turnerin-row").each(function () {
let color = getColorForProgramm($(this).data("programm"));
$(this).css({
background: color.background,
color: color.foreground
});
});
// Initial calculation on page load
updateCounters();
function groupByVereinProgramm() {
$(".geraet-table tbody").each(function () {
const tbody = $(this);
const groups = {};
tbody.find(".turnerin-row").each(function () {
const row = $(this);
const key = row.data("verein") + "||" + row.data("programm");
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(row);
});
tbody.empty();
Object.keys(groups).forEach(function (key) {
const rows = groups[key];
const verein = rows[0].data("verein");
const programm = rows[0].data("programm");
const color = getColorForProgramm(programm);
const groupRow = $(`
<div class="group-row" data-verein="${verein}" data-programm="${programm}">
<td>
<div class="group-header"
style="
background:${color.background};
color:${color.foreground};
border: 1px solid ${color.borderTop};
">
<span><b>${verein}</b><br>${programm}</span><span>(${rows.length})</span>
</div>
<table class="group-inner">
<tbody></tbody>
</table>
</td>
</div>
`);
rows.forEach(r => {
groupRow.find(".group-inner tbody").append(r);
});
tbody.append(groupRow);
});
});
updateCounters();
}
groupByVereinProgramm();
$("#groupByVereinProgramm").on("click", groupByVereinProgramm);
$("#ungroup").on("click", function () {
$(".geraet-table tbody").each(function () {
const tbody = $(this);
tbody.find(".group-row").each(function () {
const group = $(this);
group.find(".turnerin-row").appendTo(tbody);
group.remove();
});
});
updateCounters();
});
$(document).on("click", ".deleteProgramm", function (e) {
e.preventDefault();
if (window.confirm("Bitte bestätige die Löschung der Abteilung")) {
$.ajax({
url: "/intern/scripts/riegeneinteilung/ajax_delete_specific_abt.php",
method: "POST",
data: {
abt: $(this).data('abt')
}
})
.done(function (data, textStatus, jqXHR) {
if (jqXHR.status === 201) {
window.location.reload();
}
})
.fail(function (jqXHR) {
console.error("Request failed:", jqXHR.status);
});
}
});
$("#anzAbt").on("change", function () {
$.ajax({
url: "/intern/scripts/riegeneinteilung/ajax_change_number_of_abt.php",
method: "POST",
data: {
anz_abt: $(this).val()
}
})
.done(function (data, textStatus, jqXHR) {
if (jqXHR.status === 201) {
console.log("201 Created received");
window.location.reload();
}
})
.fail(function (jqXHR) {
console.error("Request failed:", jqXHR.status);
});
});
$("#autoEinteilung").on("click", function () {
$.ajax({
url: "/intern/scripts/riegeneinteilung/ajax_auto_riegeneinteilung.php",
method: "POST"
})
.done(function (data, textStatus, jqXHR) {
if (jqXHR.status === 201) {
console.log("201 Created received");
window.location.reload();;
}
})
.fail(function (jqXHR) {
console.error("Request failed:", jqXHR.status);
});
});
});
const colors = {
"0": {
"background": "rgba(172, 172, 172, 0.18)",
"foreground": "#4b4b4bff",
"borderTop": "#5a5a5aff"
},
"1": {
"background": "rgba(59, 130, 246, 0.18)",
"foreground": "#1e3a8a",
"borderTop": "#1e3a8a"
},
"2": {
"background": "rgba(20, 184, 166, 0.18)",
"foreground": "#065f46",
"borderTop": "#065f46"
},
"3": {
"background": "rgba(34, 197, 94, 0.18)",
"foreground": "#14532d",
"borderTop": "#14532d"
},
"4": {
"background": "rgba(163, 230, 53, 0.20)",
"foreground": "#365314",
"borderTop": "#365314"
},
"5": {
"background": "rgba(245, 158, 11, 0.20)",
"foreground": "#92400e",
"borderTop": "#92400e"
},
"6": {
"background": "rgba(249, 115, 22, 0.20)",
"foreground": "#9a3412",
"borderTop": "#9a3412"
},
"7": {
"background": "rgba(244, 63, 94, 0.18)",
"foreground": "#9f1239",
"borderTop": "#9f1239"
},
"8": {
"background": "rgba(236, 72, 153, 0.18)",
"foreground": "#9d174d",
"borderTop": "#9d174d"
},
"9": {
"background": "rgba(168, 85, 247, 0.18)",
"foreground": "#581c87",
"borderTop": "#581c87"
},
"10": {
"background": "rgba(99, 102, 241, 0.18)",
"foreground": "#312e81",
"borderTop": "#312e81"
},
"11": {
"background": "rgba(100, 116, 139, 0.20)",
"foreground": "#1e293b",
"borderTop": "#1e293b"
},
"12": {
"background": "rgba(6, 182, 212, 0.18)",
"foreground": "#164e63",
"borderTop": "#164e63"
}
};
</script>
<!--<iframe src="/intern/test.php" width="800px" height="600"></iframe>-->
</body>
</html>
<?php
}