557 lines
18 KiB
JavaScript
557 lines
18 KiB
JavaScript
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.addClass('opacity50');
|
|
|
|
const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_rangliste.php';
|
|
|
|
fetch(url, {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
|
body: new URLSearchParams({
|
|
csrf_token,
|
|
prog: progId,
|
|
type: fieldType
|
|
})
|
|
})
|
|
.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 }));
|
|
}
|
|
})
|
|
.then(result => {
|
|
if (result.type === 'blob') {
|
|
const url = window.URL.createObjectURL(result.data);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = "Rangliste.pdf";
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
a.remove();
|
|
window.URL.revokeObjectURL(url);
|
|
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(err => {
|
|
displayMsg(0, "Error processing request:", err);
|
|
console.error("Error processing request:", err);
|
|
$button.removeClass('opacity50');
|
|
});
|
|
|
|
});
|
|
|
|
$('.ranglisteDelete').on('click', function() {
|
|
const $button = $(this);
|
|
const progId = $button.data('id');
|
|
|
|
// 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({
|
|
csrf_token,
|
|
prog: progId
|
|
})
|
|
})
|
|
.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;
|
|
|
|
$(document).on('click', '.editableValue', function () {
|
|
const input = $(this);
|
|
|
|
// Already editing
|
|
if (!input.prop('readonly')) {
|
|
return;
|
|
}
|
|
|
|
// Close another active edit
|
|
if (activeEdit && activeEdit.get(0) !== input.get(0)) {
|
|
cancelEdit(activeEdit);
|
|
}
|
|
|
|
input.data('original', input.val());
|
|
input.prop('readonly', false).focus().select();
|
|
|
|
activeEdit = input;
|
|
|
|
input.on('keydown.edit', function (e) {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
saveEdit(input);
|
|
}
|
|
|
|
if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
cancelEdit(input);
|
|
}
|
|
});
|
|
|
|
input.on('blur.edit', function () {
|
|
saveEdit(input);
|
|
});
|
|
});
|
|
|
|
function saveEdit(input) {
|
|
const originalValue = input.data('original');
|
|
const newValue = input.val();
|
|
const type = input.data('type');
|
|
const discipline = input.data('discipline');
|
|
const dataId = input.data('id');
|
|
|
|
if (newValue === originalValue) {
|
|
input.prop('readonly', true);
|
|
cleanup(input);
|
|
return;
|
|
}
|
|
|
|
input.addClass('is-saving');
|
|
|
|
$.ajax({
|
|
url: '/intern/scripts/kampfrichter/ajax/ajax-update_value_kampfrichter_admin.php',
|
|
method: 'POST',
|
|
data: {
|
|
id: dataId,
|
|
field_type: type,
|
|
discipline: discipline,
|
|
value: newValue
|
|
},
|
|
success: function () {
|
|
input.prop('readonly', true);
|
|
|
|
const row = input.closest('td');
|
|
if (row.length) {
|
|
row.css(
|
|
'background',
|
|
'radial-gradient(circle at bottom right, #22c55e, transparent 55%)'
|
|
);
|
|
|
|
setTimeout(() => row.css('background', ''), 2000);
|
|
}
|
|
|
|
ws.send(JSON.stringify({
|
|
type: "KAMPFRICHTER_UPDATE",
|
|
payload: {
|
|
discipline: discipline,
|
|
id: dataId,
|
|
val: newValue
|
|
}
|
|
}));
|
|
},
|
|
error: function () {
|
|
input.val(originalValue).prop('readonly', true);
|
|
alert('Speichern fehlgeschlagen');
|
|
},
|
|
complete: function () {
|
|
input.removeClass('is-saving');
|
|
cleanup(input);
|
|
}
|
|
});
|
|
}
|
|
|
|
function cancelEdit(input) {
|
|
input.val(input.data('original')).prop('readonly', true);
|
|
cleanup(input);
|
|
}
|
|
|
|
function cleanup(input) {
|
|
input.off('.edit');
|
|
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');
|
|
}
|
|
})
|
|
});
|
|
}); |