Fix batch pay radio toggle: use persistent JS reference for radio group

The radio group was being removed from DOM then accessed via getElementById
which returned null for detached elements, silently breaking the toggle.
Now uses a persistent JS variable reference that survives DOM removal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Konrad du Plessis 2026-03-24 22:30:11 +02:00
parent 8d13c552aa
commit 9ebaae1b0c

View File

@ -2128,21 +2128,23 @@ document.addEventListener('DOMContentLoaded', function() {
// 'schedule' = split at last paydate (default), 'all' = pay everything unpaid // 'schedule' = split at last paydate (default), 'all' = pay everything unpaid
// --- Helper: Load batch preview for the given mode --- // --- Helper: Load batch preview for the given mode ---
// Persistent radio group reference (survives DOM removal/re-append)
var _batchRadioGroup = null;
function loadBatchPreview(mode) { function loadBatchPreview(mode) {
var body = document.getElementById('batchPayModalBody'); var body = document.getElementById('batchPayModalBody');
var footer = document.getElementById('batchPayModalFooter'); var footer = document.getElementById('batchPayModalFooter');
footer.style.display = 'none'; footer.style.display = 'none';
// Keep radio buttons if they exist, clear everything else // Clear all content from modal body
var radioGroup = document.getElementById('batchModeRadioGroup');
while (body.firstChild) body.removeChild(body.firstChild); while (body.firstChild) body.removeChild(body.firstChild);
// === Radio button group (always at top) === // === Radio button group (created once, re-appended each time) ===
if (!radioGroup) { if (!_batchRadioGroup) {
radioGroup = document.createElement('div'); _batchRadioGroup = document.createElement('div');
radioGroup.id = 'batchModeRadioGroup'; _batchRadioGroup.id = 'batchModeRadioGroup';
radioGroup.className = 'btn-group w-100 mb-3'; _batchRadioGroup.className = 'btn-group w-100 mb-3';
radioGroup.setAttribute('role', 'group'); _batchRadioGroup.setAttribute('role', 'group');
// "Until Last Paydate" radio // "Until Last Paydate" radio
var radioSchedule = document.createElement('input'); var radioSchedule = document.createElement('input');
@ -2152,7 +2154,7 @@ document.addEventListener('DOMContentLoaded', function() {
radioSchedule.id = 'batchModeSchedule'; radioSchedule.id = 'batchModeSchedule';
radioSchedule.value = 'schedule'; radioSchedule.value = 'schedule';
radioSchedule.checked = (mode === 'schedule'); radioSchedule.checked = (mode === 'schedule');
radioGroup.appendChild(radioSchedule); _batchRadioGroup.appendChild(radioSchedule);
var labelSchedule = document.createElement('label'); var labelSchedule = document.createElement('label');
labelSchedule.className = 'btn btn-outline-primary'; labelSchedule.className = 'btn btn-outline-primary';
@ -2161,7 +2163,7 @@ document.addEventListener('DOMContentLoaded', function() {
iconSchedule.className = 'fas fa-calendar-check me-1'; iconSchedule.className = 'fas fa-calendar-check me-1';
labelSchedule.appendChild(iconSchedule); labelSchedule.appendChild(iconSchedule);
labelSchedule.appendChild(document.createTextNode('Until Last Paydate')); labelSchedule.appendChild(document.createTextNode('Until Last Paydate'));
radioGroup.appendChild(labelSchedule); _batchRadioGroup.appendChild(labelSchedule);
// "Pay All" radio // "Pay All" radio
var radioAll = document.createElement('input'); var radioAll = document.createElement('input');
@ -2171,7 +2173,7 @@ document.addEventListener('DOMContentLoaded', function() {
radioAll.id = 'batchModeAll'; radioAll.id = 'batchModeAll';
radioAll.value = 'all'; radioAll.value = 'all';
radioAll.checked = (mode === 'all'); radioAll.checked = (mode === 'all');
radioGroup.appendChild(radioAll); _batchRadioGroup.appendChild(radioAll);
var labelAll = document.createElement('label'); var labelAll = document.createElement('label');
labelAll.className = 'btn btn-outline-primary'; labelAll.className = 'btn btn-outline-primary';
@ -2180,17 +2182,17 @@ document.addEventListener('DOMContentLoaded', function() {
iconAll.className = 'fas fa-list me-1'; iconAll.className = 'fas fa-list me-1';
labelAll.appendChild(iconAll); labelAll.appendChild(iconAll);
labelAll.appendChild(document.createTextNode('Pay All')); labelAll.appendChild(document.createTextNode('Pay All'));
radioGroup.appendChild(labelAll); _batchRadioGroup.appendChild(labelAll);
// Re-fetch preview when radio changes // Re-fetch preview when radio changes
radioSchedule.addEventListener('change', function() { loadBatchPreview('schedule'); }); radioSchedule.addEventListener('change', function() { loadBatchPreview('schedule'); });
radioAll.addEventListener('change', function() { loadBatchPreview('all'); }); radioAll.addEventListener('change', function() { loadBatchPreview('all'); });
} else { } else {
// Update checked state on existing radios // Update checked state using the saved reference (not getElementById — element is detached)
document.getElementById('batchModeSchedule').checked = (mode === 'schedule'); _batchRadioGroup.querySelector('#batchModeSchedule').checked = (mode === 'schedule');
document.getElementById('batchModeAll').checked = (mode === 'all'); _batchRadioGroup.querySelector('#batchModeAll').checked = (mode === 'all');
} }
body.appendChild(radioGroup); body.appendChild(_batchRadioGroup);
// Show loading spinner below radio buttons // Show loading spinner below radio buttons
var loadDiv = document.createElement('div'); var loadDiv = document.createElement('div');
@ -2344,9 +2346,8 @@ document.addEventListener('DOMContentLoaded', function() {
if (batchPayBtn) { if (batchPayBtn) {
batchPayBtn.addEventListener('click', function() { batchPayBtn.addEventListener('click', function() {
var modal = new bootstrap.Modal(document.getElementById('batchPayModal')); var modal = new bootstrap.Modal(document.getElementById('batchPayModal'));
// Reset radio group so it gets re-created fresh // Reset radio group so it gets re-created fresh each time modal opens
var oldRadio = document.getElementById('batchModeRadioGroup'); _batchRadioGroup = null;
if (oldRadio) oldRadio.remove();
modal.show(); modal.show();
// Default mode: split at last paydate // Default mode: split at last paydate
loadBatchPreview('schedule'); loadBatchPreview('schedule');