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'); } }) }); });