Semi-stable version with old variable names

This commit is contained in:
Fabio Herzig
2026-07-16 18:43:30 +02:00
parent 3731183654
commit c8e7ce9174
165 changed files with 25654 additions and 9728 deletions
@@ -1,11 +1,20 @@
jQuery(document).ready(function($) {
const csrf_token = window.CSDR_TOKEN;
const allowedRanglistenTypes = ['downloadRangliste', 'updateServerRangliste'];
$('.ranglisteExport').on('click', function() {
const $button = $(this);
const progId = $button.data('id');
const fieldType = $button.data('field_type');
if (!allowedRanglistenTypes.includes(fieldType)) {
displayMsg(0, "Dieser Button besitzt eine fehlerhafte Konfiguration");
return;
}
// Visual feedback (optional but recommended)
$button.prop('disabled', true).text('Generating...');
$button.addClass('opacity50');
const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_rangliste.php';
@@ -13,81 +22,193 @@ jQuery(document).ready(function($) {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
prog: progId,
type: fieldType
})
})
.then(response => {
// CRITICAL: Check if the server actually returned a success code
if (!response.ok) {
throw new Error('Server returned an error (Status ' + response.status + ')');
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
// Return both the response and the blob/json promise
// to keep the chain clean, or handle them based on fieldType here.
if (fieldType === 'downloadRangliste') {
return res.blob().then(blob => ({ type: 'blob', data: blob }));
} else {
return res.json().then(json => ({ type: 'json', data: json }));
}
return response.blob();
})
.then(blob => {
if (fieldType !== 'upload_programm') {
const url = window.URL.createObjectURL(blob);
.then(result => {
if (result.type === 'blob') {
const url = window.URL.createObjectURL(result.data);
const a = document.createElement('a');
a.href = url;
a.download = `Ergebnisse_${progId}.pdf`;
a.download = "Rangliste.pdf";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
a.remove();
window.URL.revokeObjectURL(url);
} else {
alert('PDF auf Webseite geladen!');
displayMsg(1, "Rangliste wurde erfolgreich heuntergeladen.");
} else if (result.type === 'json') {
if (result.data.success) {
const $container = $button.closest(".singleAbtDiv");
$container.find(".uploadRanglisteText").addClass("hidden");
$container.find(".updateRanglisteText").removeClass("hidden");
$container.find(".rangliseOnlineSpan").removeClass("hidden");
$container.find(".ranglisteDelete").removeClass("hidden");
displayMsg(1, "Die Rangliste wurde auf dem Server aktualisiert.");
} else {
displayMsg(0, "Fehler beim Aktualiseren der Rangliste.");
}
}
$button.removeClass('opacity50');
})
.catch(error => {
console.error('Export Error:', error);
alert('Fehler beim Exportieren der Rangliste.');
})
.finally(() => {
// Restore button state
$button.prop('disabled', false).text('Export PDF');
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
$button.removeClass('opacity50');
});
});
$('.protokollExport').on('click', function() {
console.log('ok');
const $input = $(this);
// Build the data to send
const data = new URLSearchParams();
data.append('abteilung', $input.data('abteilung'));
// Record start time
const start = performance.now();
$('.ranglisteDelete').on('click', function() {
const $button = $(this);
const progId = $button.data('id');
const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_protokoll.php';
// Visual feedback (optional but recommended)
$button.addClass('opacity50');
const url = '/intern/scripts/kampfrichter/ajax/ajax-delete_rangliste.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
abteilung: $input.data('abteilung')
csrf_token,
prog: progId
})
})
.then(res => res.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = "KTBB_Protokoll.pdf"; // optional
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
return res.json().then(json => ({ type: 'json', data: json }));
})
.then(result => {
if (result.data.success) {
const $container = $button.closest(".singleAbtDiv");
$container.find(".uploadRanglisteText").removeClass("hidden");
$container.find(".updateRanglisteText").addClass("hidden");
$container.find(".rangliseOnlineSpan").addClass("hidden");
$container.find(".ranglisteDelete").addClass("hidden");
displayMsg(1, "Die Rangliste wurde erfolgreich entfernt.");
} else {
displayMsg(0, "Fehler beim Löschen der Rangliste.");
}
$button.removeClass('opacity50');
})
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
$button.removeClass('opacity50');
});
});
$('.protokollExport').on('click', function() {
const $button = $(this);
const abteilungId = $button.data('abteilung-id');
const now = new Date();
const formattedDate = new Intl.DateTimeFormat('en-CA', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).format(now).replace(/,/g, '');
const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_protokoll.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
abteilungId: abteilungId,
date: formattedDate
})
})
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
return res.blob().then(blob => ({ type: 'blob', data: blob }));
})
.then(result => {
if (result.type === 'blob') {
const url = window.URL.createObjectURL(result.data);
const a = document.createElement('a');
a.href = url;
a.download = "Protokoll.pdf";
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
displayMsg(1, "Protokoll wurde erfolgreich heuntergeladen.");
}
})
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
});
});
$('.protokollDeleteEntries').on('click', async function() {
const $button = $(this);
const abt = $button.data('abteilung-id');
if (!await displayConfirmImportant("Möchten Sie wirklich alle Protokolleinträge für diese Abteilung löschen? Diese Aktion kann nicht rückgängig gemacht werden.")) { return; }
const url = '/intern/scripts/kampfrichter/ajax/ajax-delete_protokoll_entries.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
abteilungId: abt
})
})
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
return res.json().then(json => ({ type: 'json', data: json }));
})
.then(result => {
if (result.data.success) {
displayMsg(1, "Die Einträge wurden erfolgreich entfernt.");
} else {
displayMsg(0, "Fehler beim Löschen der Einträge.");
}
})
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
});
});
let activeEdit = null;
console.log('pre');
$(document).on('click', '.editableValue', function () {
console.log('ok');
const input = $(this);
// Already editing
@@ -189,4 +310,248 @@ jQuery(document).ready(function($) {
activeEdit = null;
}
let rangNotenArray = JSON.parse(window.RANG_NOTEN_ARRAY) ?? [];
const rangNotenId = window.RANG_NOTE_ID ?? 0;
const rangSort = window.RANG_SORT ?? '';
const selecteddiscipline = window.FREIGABE;
ws.addEventListener("message", event => { // Use 'event' as it's more standard than 'blob'
let msgOBJ;
try {
msgOBJ = JSON.parse(event.data);
} catch (error) {
return;
}
// Ensure it's an UPDATE type (matches your sendToGroup logic)
if (msgOBJ?.type === "UPDATE") {
const data = msgOBJ.payload;
// Check access rights
if (selecteddiscipline === 'A') {
const noten = data.noten;
if (noten[0][rangNotenId][1] !== undefined && rangNotenArray[data.programmId][data.personId] !== undefined) {
rangNotenArray[data.programmId][data.personId] = noten[0][rangNotenId][1];
applyRanks(rankProgramm(rangNotenArray, data.programmId, rangSort), true);
}
}
}
});
function rankProgramm(dataObj, programmId, order = 'DESC') {
if (!dataObj[programmId]) {
console.error(`ID ${programmId} not found in data.`);
return [];
}
const values = dataObj[programmId];
// 1. Transform and Sort
const sorted = Object.entries(values)
.map(([id, val]) => ({ id, val }))
.sort((a, b) => {
// 1. Handle null values first (Always push them to the bottom)
if (a.val === null && b.val === null) return 0;
if (a.val === null) return 1; // Move 'a' to a higher index (down)
if (b.val === null) return -1; // Move 'b' to a higher index (down)
// 2. Normal sorting logic for valid numbers
return order.toUpperCase() === 'ASC'
? a.val - b.val
: b.val - a.val;
});
let lastVal = null;
let lastRank = 0;
let sortedMap = [];
sortedMap[programmId] = sorted.map((item, index) => {
// If current value is different from the last, update rank to current position
if (item.val !== lastVal) {
lastRank = index + 1;
}
lastVal = item.val;
return {
rang: lastRank,
...item
};
});
return sortedMap;
}
function rankAll(dataObj, order = 'DESC') {
return Object.fromEntries(
Object.entries(dataObj).map(([groupKey, values]) => {
const sorted = Object.entries(values)
.map(([id, val]) => ({ id, val }))
.sort((a, b) => {
// 1. Handle null values first (Always push them to the bottom)
if (a.val === null && b.val === null) return 0;
if (a.val === null) return 1; // Move 'a' to a higher index (down)
if (b.val === null) return -1; // Move 'b' to a higher index (down)
// 2. Normal sorting logic for valid numbers
return order.toUpperCase() === 'ASC'
? a.val - b.val
: b.val - a.val;
});
let lastVal = null;
let lastRank = 0;
const ranked = sorted.map((item, index) => {
if (item.val !== lastVal) {
lastRank = index + 1;
}
lastVal = item.val;
return {
rang: lastRank,
...item
};
});
return [groupKey, ranked];
})
);
}
function applyRanks(array, sort = false) {
for (const [key, entries] of Object.entries(array)) {
const $tbody = $(`.customDisplayEditorTable tbody[data-programm-id="${key}"]`);
if ($tbody.length === 0) continue;
for (const [skey, entry] of Object.entries(entries)) {
const $row = $tbody.find(`tr[data-person-id="${entry.id}"]:not(.notpaid)`);
if ($row.length === 0) continue;
$row.attr('data-sort-rank', entry.rang);
const $cell = $row.find('.rangField');
const points = parseFloat(rangNotenArray[key][entry.id]);
const text = !isNaN(points) ? entry.rang : '';
$cell.text(text);
}
if (sort) {
const rows = $tbody.find('tr').get();
rows.sort((a, b) => {
const rankA = parseFloat($(a).attr('data-sort-rank')) || 999;
const rankB = parseFloat($(b).attr('data-sort-rank')) || 999;
return rankA - rankB;
});
$.each(rows, (index, row) => {
$tbody.append(row);
});
}
}
}
applyRanks(rankAll(rangNotenArray, rangSort), true);
window.addEventListener('valueChanged', (e) => {
// Use e.detail to get your object
const { noten, programmId, personId } = e.detail;
if (noten[0][rangNotenId][1] !== undefined && rangNotenArray[programmId][personId] !== undefined) {
rangNotenArray[programmId][personId] = noten[0][rangNotenId][1];
applyRanks(rankProgramm(rangNotenArray, programmId, rangSort), true);
}
});
const $parent = $('.allAbtContainer');
const $children = $parent.children('.shiftedGeraetTable');
$children.sort((a, b) => {
const idA = parseFloat($(a).data('geraet-index'));
const idB = parseFloat($(b).data('geraet-index'));
return idA - idB;
});
$parent.append($children);
const wsMessage = window.WS_MESSAGE || '';
if (wsMessage !== '') {
ws.addEventListener('open', () => {
setTimeout(() => {
ws.send(wsMessage);
}, 300);
});
}
$('.deleteNotenAbt').on('click', async function () {
if (!(await displayConfirmImportant('Alle Noten dieses Programmes wirklich löschen? Diese Aktion ist unumkehrbar!'))) return;
const $btn = $(this);
const programmId = parseInt($btn.data('programm-id') || 0);
$.ajax({
url: '/intern/scripts/kampfrichter/ajax/ajax-delete-noten-turnerinnen.php',
type: 'DELETE',
data: {
csrf_token,
programmId
},
success: function (response) {
if (response.success) {
const $tbody = $btn.closest('.singleAbtDiv').find('tbody');
if ($tbody.length !== 1 || $tbody.data('programm-id') !== programmId) {
location.reload();
return;
}
const $allValues = $tbody.find('.changebleValue:not(.emtyPlaceholder)');
$allValues.each((ind, el) => {
$(el).text('-').addClass('emtyPlaceholder');
updateDependentVisibility($(el));
});
const $allRanks = $tbody.find('.rangField');
$allRanks.each((ind, el) => {
$(el).text('');
});
const $allRows = $tbody.find('tr');
$allRows.each((ind, el) => {
$(el).attr('data-sort-rank', "0");
rangNotenArray[programmId][$(el).attr('data-person-id')] = null;
});
displayMsg(1, 'Noten gelöscht');
} else {
displayMsg(0, response.message || 'Fehler beim Löschen');
}
},
error: function () {
displayMsg(0, 'Fehler beim Löschen');
}
})
});
});
@@ -5,6 +5,50 @@ document.addEventListener('keydown', function (e) {
if (isOptionOrAlt && e.shiftKey && e.key.toLowerCase() === 'f') {
toggleFullscreen();
}
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault();
}
});
const menuburger = document.querySelector('.kampfrichterBurgerMenuDiv');
const menudiv = document.querySelector('.internMenuDiv');
const closeMenuMobile = document.querySelector('.closeInternMenuMobileDiv');
const storageKey = "kampfrichterInternMenuOpen";
const isOpen = localStorage.getItem(storageKey) === "true";
if (isOpen) {
menuburger.classList.add("open");
menudiv.classList.add("open");
}
menuburger.addEventListener("click", function () {
menuburger.classList.add("menuTransition");
menudiv.classList.add("menuTransition");
menuburger.classList.toggle("open");
menudiv.classList.toggle("open");
const isOpenEl =
menudiv.classList.contains("open") &&
menuburger.classList.contains("open");
localStorage.setItem(storageKey, isOpenEl);
});
closeMenuMobile.addEventListener("click", function () {
const isOpenEl =
menudiv.classList.contains("open") &&
menuburger.classList.contains("open");
if (isOpenEl) {
menuburger.classList.remove("open");
menudiv.classList.remove("open");
localStorage.setItem(storageKey, false);
}
});
function toggleFullscreen() {
@@ -22,61 +66,33 @@ function toggleFullscreen() {
let messagePosArray = [];
const csrf_token = window.CSDR_TOKEN;
function displayMsg(type, msg) {
const colors = ["#900000ff", "#00b200ff"];
if (type !== 0 && type !== 1) return;
// idx is ALWAYS a valid non-negative index now
const $div = $('<div class="msgBox"></div>')
.css({'border-color': colors[type]})
.text(msg);
$('.msgDiv').append($div);
// trigger entry animation
setTimeout(() => {
$div.addClass('show');
}, 200);
setTimeout(() => {
// First: set the transition properties
$div.css({
'transition': 'all 0.3s ease'
});
// Next frame: apply the transform so the transition animates
requestAnimationFrame(() => {
$div.removeClass('show');
$div.css({
'transform': 'scaleY(0) translateX(calc(100% + 40px))'
});
});
}, 3000);
// auto-remove
setTimeout(() => {
$div.remove();
}, 3500);
}
const text = document.getElementById('wsInfo');
const rect = document.getElementById('wsInfoRectangle');
let ws;
let firstConnect = true;
let wsOpen = false;
const RETRY_DELAY = 2000; // ms
const WSaccesstype = "kampfrichter";
const WSaccess = window.FREIGABE;
const WSaccessPermission = "W";
const urlAjaxNewWSToken = '/intern/scripts/ajax-create-ws-token.php';
async function fetchNewWSToken(freigabe) {
async function fetchNewWSToken(accesstype, access, accessPermission) {
try {
const response = await fetch(urlAjaxNewWSToken, {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ access: freigabe, csrf_token })
body: new URLSearchParams({
csrf_token,
accesstype,
access,
accessPermission
})
});
if (response.status === 403) {
@@ -102,7 +118,7 @@ async function startWebSocket() {
if (firstConnect) {
token = window.WS_ACCESS_TOKEN;
} else {
token = await fetchNewWSToken('kampfrichter');
token = await fetchNewWSToken(WSaccesstype, WSaccess, WSaccessPermission);
}
if (!token) {
@@ -125,6 +141,8 @@ async function startWebSocket() {
displayMsg(1, "Live Syncronisation wieder verfügbar");
}
firstConnect = true;
wsOpen = true;
rect.innerHTML =
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#28a745"></circle><path d="M7 12l3 3 7-7" stroke="white" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
@@ -138,8 +156,13 @@ async function startWebSocket() {
};
ws.onclose = (event) => {
displayMsg(0, "Live Syncronisation verloren");
if (firstConnect) {
displayMsg(0, "Live Syncronisation verloren");
console.log(event);
}
firstConnect = false;
wsOpen = false;
rect.innerHTML =
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#dc3545"/><path d="M8 8l8 8M16 8l-8 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
@@ -167,6 +190,8 @@ function updateRunButtons(targetCount, personId, $container) {
const currentCount = $container.find('.submit-display-result').length;
$container.find('.submit-display-result').removeClass('hidden');
if (targetCount > currentCount) {
for (let i = currentCount + 1; i <= targetCount; i++) {
const buttonHtml = `
@@ -214,6 +239,11 @@ $.fn.updateCurrentEdit = function() {
name: 'next_subabt_submit',
value: '>'
}))
.append($('<input>', {
type: 'hidden',
name: 'csrf_token',
value: csrf_token
}))
.appendTo('body')
.submit();
}
@@ -256,6 +286,9 @@ $.fn.updateCurrentEdit = function() {
const $editAllDiv = $('.div_edit_values_all_gereate');
const noten = response.noten;
const personId = response.id;
const programmId = response.programm_id;
$editAllDiv.find(`.submit-display-turnerin`).closest('.all_vaules_div').addClass('hidden');
// 1. Loop directly through the 'noten' object
for (const [geraetId, disciplineData] of Object.entries(noten)) {
@@ -266,6 +299,8 @@ $.fn.updateCurrentEdit = function() {
if ($disciplineWrapper.length === 0) continue;
$disciplineWrapper.removeClass('hidden');
// --- UPDATE GENERAL BUTTONS FOR THIS GERAET ---
$disciplineWrapper.find(".submit-display-turnerin, .submit-display-start").attr({
'data-person-id': personId,
@@ -280,17 +315,18 @@ $.fn.updateCurrentEdit = function() {
// 2. Identify master containers for this specific discipline
const $masterContainer = $disciplineWrapper.find('.singleNotentable').first();
const $displayresultDiv = $disciplineWrapper.find('.div-submit-display-result');
$masterContainer.find('.note-container').addClass('hidden');
// 3. CLEANUP: Remove previously generated runs and buttons
$disciplineWrapper.find('.singleNotentable').not(':first').remove();
$displayresultDiv.find('.submit-display-result').not(':first').remove();
const $originalResultBtn = $displayresultDiv.find('.submit-display-result').first();
$displayresultDiv.find('.submit-display-result').addClass('hidden');
const runKeys = Object.keys(disciplineData).sort((a, b) => a - b);
const totalRuns = runKeys.length;
console.log(totalRuns);
// 4. Process each Run in the data
runKeys.forEach(runNum => {
const runInt = parseInt(runNum);
@@ -310,6 +346,7 @@ $.fn.updateCurrentEdit = function() {
// 5. Update all Tables and Inputs inside this Run Container
for (const [noteId, value] of Object.entries(disciplineData[runNum])) {
const $table = $currentRunContainer.find(`.note-container[data-note-id="${noteId}"]`);
$table.removeClass('hidden');
// Update Header to show Run Number
if (runInt > 1) {
@@ -324,7 +361,8 @@ $.fn.updateCurrentEdit = function() {
$input.attr({
'data-run': runNum,
'data-person-id': personId,
'data-geraet-id': geraetId
'data-geraet-id': geraetId,
'data-programm-id': programmId
}).val(value ?? '');
}
@@ -362,6 +400,10 @@ $.fn.updateCurrentEdit = function() {
});
};
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
jQuery(document).ready(function($) {
@@ -370,16 +412,17 @@ jQuery(document).ready(function($) {
});
const $ajaxInputDiv = $('.div_edit_values_all_gereate');
$ajaxInputDiv.on('change', '.ajax-input', function(e) {
const start = performance.now();
const $input = $(this);
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_value_kampfrichter.php`;
personId = $input.data('person-id');
fieldTypeId = $input.data('field-type-id');
gereatId = $input.data('geraet-id');
personId = $input.attr('data-person-id');
fieldTypeId = $input.attr('data-field-type-id');
programmId = $input.attr('data-programm-id');
gereatId = $input.attr('data-geraet-id');
runNum = $input.attr('data-run') || 1;
jahr = window.AKTUELLES_JAHR;
value = $input.val();
@@ -399,8 +442,6 @@ jQuery(document).ready(function($) {
})
.then(res => res.json())
.then(response => {
const end = performance.now();
console.log(`Total AJAX time: ${(end - start).toFixed(3)} ms`);
if (response.success) {
@@ -415,32 +456,63 @@ jQuery(document).ready(function($) {
const noten = response.noten;
for (const [keyN, noteGroup] of Object.entries(noten)) {
for (const [keyG, noteGroup] of Object.entries(noten)) {
for (const [key, runGroup] of Object.entries(noteGroup)) {
for (const [run, value] of Object.entries(runGroup)) {
const selectorBase = `[data-field-type-id="${key}"][data-geraet-id="${keyN}"][data-person-id="${personId}"][data-run="${run}"]`;
// Handle Inputs (excluding current one)
$(`input.changebleValue${selectorBase}`)
.not(this)
.val(value ?? '');
// Handle Display elements (Spans/Divs)
$(`.changebleValue:not(input)${selectorBase}`)
.text(value ?? '');
const $elements = $(`.changebleValue[data-field-type-id="${key}"][data-geraet-id="${keyG}"][data-person-id="${personId}"][data-run="${run}"]`);
$elements.each((ind, el) => {
const $el = $(el);
if ($el.is('input')) {
$el.val(value ?? '');
} else {
const isNullable = ($el.attr('data-noten-nullable') ?? 'false') === 'true';
const floatValZero = parseFloat(value.replace(",", ".").replace("'", "")) === 0.0;
let hasValue = false;
if (isNullable && floatValZero || value === '') {
$el.text('-').addClass('emtyPlaceholder');
} else {
$el.text(value).removeClass('emtyPlaceholder');
hasValue = true;
}
updateDependentVisibility($el, hasValue);
}
});
}
}
}
ws.send(JSON.stringify({
type: "KAMPFRICHTER_UPDATE",
payload: {
discipline: window.FREIGABE,
gereatId: gereatId,
personId: personId,
jahr: jahr,
noten: noten
}
}));
if (selecteddiscipline === 'A') {
const event = new CustomEvent('valueChanged', {
detail: {
noten: noten,
programmId: programmId,
personId: personId
}
});
window.dispatchEvent(event);
}
if (wsOpen) {
ws.send(JSON.stringify({
type: "KAMPFRICHTER_UPDATE",
payload: {
discipline: window.FREIGABE,
gereatId: gereatId,
personId: personId,
programmId: programmId,
jahr: jahr,
noten: noten
}
}));
}
} else {
// Flash red on error
@@ -455,59 +527,9 @@ jQuery(document).ready(function($) {
});
});
$('.inputnamekr').on('change', function() {
const $input = $(this);
const url = '/intern/scripts/kampfrichter/ajax/ajax-update_name_kampfrichter_protokoll.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
aufgabe: $input.data('id'),
abteilung: $input.data('abt'),
geraet: $input.data('user'),
name: $input.val()
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
console.log(response.message);
$input.css({
'background-color': '#a4bf4a',
'color': '#fff',
'transition': 'all 0.3s ease-out'
});
setTimeout(() => $input.css({
'background-color': '',
'color': ''
}), 2000);
} else {
console.error(response.message);
$input.css({
'background-color': '#f8d7da',
'color': '#fff',
'transition': 'all 0.3s ease-out'
});
setTimeout(() => $input.css({
'background-color': '',
'color': ''
}), 2000);
}
})
.catch(err => {
$input.css('background-color', '#f8d7da');
console.error('AJAX fetch error:', err);
displayMsg(0, 'AJAX fetch error:' + err);
});
});
$('.submit-display-turnerin').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
// Build the URL with GET parameters safely
const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php';
@@ -518,7 +540,7 @@ jQuery(document).ready(function($) {
body: new URLSearchParams({
csrf_token,
personId: $input.attr('data-person-id'),
geraetId: $input.attr('data-geraet-id'),
geraetId,
jahr: window.AKTUELLES_JAHR,
type: "neu"
})
@@ -527,14 +549,19 @@ jQuery(document).ready(function($) {
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
data: response.data
}
}));
displayMsg(1, 'Neue Turnerin wird angezigt');
if (wsOpen) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
geraetId,
data: response.data
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
displayMsg(1, 'Neue Person wird angezigt');
$input.css('opacity', 0.5);
} else {
@@ -550,6 +577,8 @@ jQuery(document).ready(function($) {
$('.submit-display-start').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
const personId = $input.attr('data-person-id')
const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php';
const dataType = $input.attr('data-type');
@@ -559,8 +588,8 @@ jQuery(document).ready(function($) {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
geraetId: $input.attr('data-geraet-id'),
personId: $input.attr('data-person-id'),
geraetId,
personId,
dataType: dataType,
type: "start"
})
@@ -569,13 +598,18 @@ jQuery(document).ready(function($) {
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
data: response.data
}
}));
if (wsOpen) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
geraetId,
data: response.data
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
if (dataType == 1) {
displayMsg(1, 'Start freigegeben');
@@ -598,6 +632,7 @@ jQuery(document).ready(function($) {
$('.submit-musik-start').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
// Build the URL with GET parameters safely
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_kampfrichter_start_musik.php`;
@@ -607,17 +642,22 @@ jQuery(document).ready(function($) {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
id: $input.attr('data-id'),
discipline: $input.data('geraet')
personId: $input.attr('data-id'),
geraetId
})
})
.then(res => res.json())
.then(response => {
const end = performance.now();
ws.send(JSON.stringify({
type: "AUDIO",
payload: {}
}));
if (wsOpen) {
ws.send(JSON.stringify({
type: "AUDIO",
payload: {
audioDiscipline: geraetId
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
if (response.success) {
displayMsg(1, 'Musik wird abgespielt werden');
@@ -634,6 +674,7 @@ jQuery(document).ready(function($) {
$('.submit-musik-stopp').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
// Build the URL with GET parameters safely
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_kampfrichter_stopp_musik.php`;
@@ -642,16 +683,23 @@ jQuery(document).ready(function($) {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token
csrf_token,
geraetId
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "AUDIO",
payload: {}
}));
if (wsOpen) {
ws.send(JSON.stringify({
type: "AUDIO",
payload: {
audioDiscipline: geraetId
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
displayMsg(1, 'Musik wird gestoppt werden');
} else {
alert('Error: ' + response.message);
@@ -666,6 +714,8 @@ jQuery(document).ready(function($) {
$('.div-submit-display-result').on('click', '.submit-display-result', function() {
$input = $(this);
const geraetId = $input.attr('data-geraet-id');
const personId = $input.attr('data-person-id');
// Build the URL with GET parameters safely
const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php';
@@ -675,8 +725,8 @@ jQuery(document).ready(function($) {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
personId: $input.attr('data-person-id'),
geraetId: $input.attr('data-geraet-id'),
personId,
geraetId,
run: $input.attr("data-run"),
jahr: window.AKTUELLES_JAHR,
type: "result"
@@ -686,13 +736,27 @@ jQuery(document).ready(function($) {
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
data: response.data
}
}));
if (wsOpen) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraetId,
geraet: response.nameGeraet,
data: response.data
}
}));
ws.send(JSON.stringify({
type: "UPDATE_RANKLIVE_SCORE",
payload: {
geraetId,
personId,
jahr: window.AKTUELLES_JAHR,
rankLive: response.rankLive
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
$input.css('opacity', 0.5);
displayMsg(1, 'Resultat wird angezeigt');
} else {
@@ -707,6 +771,58 @@ jQuery(document).ready(function($) {
});
});
function updateDependentVisibility($changedEl, hasValue = false) {
const uid = $changedEl.attr('data-uid');
if (uid === undefined || uid === null) return;
const $row = $changedEl.closest('tr');
if ($row.length !== 1) return;
const isVisible = $changedEl.hasClass('emtyPlaceholder');
const doesExist = $changedEl.hasClass('nonExistentEl');
const $linkedEls = $row.find(`.singleValueSpan[data-linked-el-uid="${uid}"]`);
$linkedEls.each((ind, el) => {
const $linkedEl = $(el);
if (!hasValue) {
hasValue = $linkedEl.hasClass('changebleValue') && $linkedEl.text().trim() !== '-';
}
$linkedEl.toggleClass('emtyPlaceholder', (isVisible && !hasValue)).toggleClass('nonExistentEl', doesExist);
const linkedElUid = $linkedEl.data('linked-el-uid');
const $subLinkedEls = $row.find(`.singleValueSpan[data-linked-el-uid="${linkedElUid}"]`);
if ($subLinkedEls.length > 0) updateDependentVisibility($linkedEl);
});
}
function initConditionalVisibility() {
const $table = $('table.customDisplayEditorTable');
const $rows = $table.find('tr');
$rows.each((ind, el) => {
const $row = $(el);
$row.find('.singleValueSpan[data-linked-el-uid]').each(function() {
const linkedUid = $(this).data('linked-el-uid');
const $linkedEl = $row.find(`.singleValueSpan[data-uid="${linkedUid}"]`);
if ($linkedEl.length === 1) {
updateDependentVisibility($linkedEl);
}
});
})
}
initConditionalVisibility();
initConditionalVisibility();
const selecteddiscipline = window.FREIGABE;
@@ -724,39 +840,53 @@ ws.addEventListener("message", event => { // Use 'event' as it's more standard t
const data = msgOBJ.payload;
// Check access rights
if (data.discipline === selecteddiscipline.toLowerCase() || selecteddiscipline.toLowerCase() === 'admin') {
if (data.discipline === selecteddiscipline.toLowerCase() || selecteddiscipline === 'A') {
const noten = data.noten;
for (const [keyG, noteGroup] of Object.entries(noten)) {
for (const [key, runGroup] of Object.entries(noteGroup)) {
for (const [run, value] of Object.entries(runGroup)) {
// OPTIONAL: Skip if the current user is currently focused on this specific input
if (document.activeElement.dataset.fieldTypeId === key &&
document.activeElement.dataset.geraetId === keyG &&
document.activeElement.dataset.run == run) continue;
// Select all matching elements (inputs and spans)
const $elements = $(`.changebleValue[data-field-type-id="${key}"][data-geraet-id="${keyG}"][data-person-id="${data.personId}"][data-run="${run}"]`);
$elements.each(function() {
const $el = $(this);
$elements.each((ind, el) => {
const $el = $(el);
// Update value or text
if ($el.is('input')) {
$el.val(value ?? '');
} else {
$el.text(value ?? '');
}
// Visual feedback: Flash color
const nativeEl = $el[0];
nativeEl.style.transition = 'color 0.3s';
nativeEl.style.color = '#008e85';
setTimeout(() => {
nativeEl.style.color = '';
}, 1000);
// Visual feedback: Flash color
const nativeEl = $el[0];
nativeEl.style.transition = 'color 0.3s';
nativeEl.style.color = '#008e85';
setTimeout(() => {
nativeEl.style.color = '';
}, 1000);
} else {
const isNullable = ($el.attr('data-noten-nullable') ?? 'false') === 'true';
const floatValZero = parseFloat(value.replace(",", ".").replace("'", "")) === 0.0;
let hasValue = false;
if (isNullable && floatValZero || value === '') {
$el.text('-').addClass('emtyPlaceholder');
} else {
$el.text(value).removeClass('emtyPlaceholder');
hasValue = true;
}
updateDependentVisibility($el, hasValue);
$el.removeClass('updated');
$el.addClass('updated');
setTimeout(() => {
$el.removeClass('updated');
}, 2000);
}
});
}
}
@@ -1,34 +0,0 @@
function allNameKrValid() {
var isValid = true;
$('.ajax-input-namekr').each(function () {
if ($(this).val() === '---') {
isValid = false;
return false; // break loop
}
});
return isValid;
}
function updateSelectKrDiv() {
const krDiv = $('.noKampfrichterDiv');
if (krDiv.length !== 1) {
return;
}
if (allNameKrValid()) {
krDiv.css('display', 'none');
} else {
krDiv.css('display', 'flex');
}
}
// initial check
updateSelectKrDiv();
// react to changes
$('.ajax-input-namekr').on('change', function () {
updateSelectKrDiv();
});