diff --git a/application/views/bandmap/list.php b/application/views/bandmap/list.php index e3cfd5ceb..bb88466ab 100644 --- a/application/views/bandmap/list.php +++ b/application/views/bandmap/list.php @@ -113,6 +113,9 @@ var lang_bandmap_spot = ""; var lang_bandmap_spotters = ""; + // QSO preparation debounce messages + var lang_bandmap_please_wait = ""; + var lang_bandmap_wait_before_send = ""; // DataTables messages var lang_bandmap_loading_spots = ""; @@ -518,7 +521,6 @@ "> "> "> - "> "> "> "> diff --git a/assets/css/bandmap_list.css b/assets/css/bandmap_list.css index 5ee5a46c5..fbfc4580b 100644 --- a/assets/css/bandmap_list.css +++ b/assets/css/bandmap_list.css @@ -342,22 +342,21 @@ table tbody tr.cat-nearest-above td { /* Column widths - consolidated selectors */ .spottable th:nth-child(1), .spottable td:nth-child(1) { width: 50px; } /* Age (minutes) */ -.spottable th:nth-child(2), .spottable td:nth-child(2) { width: 53px; } /* Band */ +.spottable th:nth-child(2), .spottable td:nth-child(2) { width: 75px; } /* Band */ .spottable th:nth-child(3), .spottable td:nth-child(3) { width: 90px; } /* Frequency */ -.spottable th:nth-child(4), .spottable td:nth-child(4) { width: 60px; } /* Mode */ -.spottable th:nth-child(5), .spottable td:nth-child(5) { width: 70px; } /* Submode */ -.spottable th:nth-child(6), .spottable td:nth-child(6) { width: 115px; } /* Callsign (reduced by 5px) */ +.spottable th:nth-child(4), .spottable td:nth-child(4) { width: 70px; } /* Mode */ +.spottable th:nth-child(5), .spottable td:nth-child(5) { width: 60px; } /* Submode */ +.spottable th:nth-child(6), .spottable td:nth-child(6) { width: 115px; } /* Callsign */ .spottable th:nth-child(7), .spottable td:nth-child(7) { width: 40px; } /* Continent */ .spottable th:nth-child(8), .spottable td:nth-child(8) { width: 50px; } /* CQ Zone */ .spottable th:nth-child(9), .spottable td:nth-child(9) { width: 50px; } /* Flag */ .spottable th:nth-child(10), .spottable td:nth-child(10) { width: 150px; } /* Entity (DXCC name) */ -.spottable th:nth-child(11), .spottable td:nth-child(11) { width: 60px; } /* DXCC Number */ -.spottable th:nth-child(12), .spottable td:nth-child(12) { width: 115px; } /* de Callsign (Spotter) (reduced by 5px) */ -.spottable th:nth-child(13), .spottable td:nth-child(13) { width: 50px; } /* de Cont */ -.spottable th:nth-child(14), .spottable td:nth-child(14) { width: 50px; } /* de CQZ */ -.spottable th:nth-child(15), .spottable td:nth-child(15) { width: 95px; } /* Last QSO */ -.spottable th:nth-child(16), .spottable td:nth-child(16) { width: 120px; } /* Special (LoTW, POTA, etc) (increased by 10px) */ -.spottable th:nth-child(17), .spottable td:nth-child(17) { min-width: 70px; width: 100%; } /* Message - fills remaining space */ +.spottable th:nth-child(11), .spottable td:nth-child(11) { width: 115px; } /* de Callsign (Spotter) */ +.spottable th:nth-child(12), .spottable td:nth-child(12) { width: 50px; } /* de Cont */ +.spottable th:nth-child(13), .spottable td:nth-child(13) { width: 50px; } /* de CQZ */ +.spottable th:nth-child(14), .spottable td:nth-child(14) { width: 100px; } /* Last QSO */ +.spottable th:nth-child(15), .spottable td:nth-child(15) { width: 135px; } /* Special (LoTW, POTA, etc) */ +.spottable th:nth-child(16), .spottable td:nth-child(16) { min-width: 70px; width: 100%; } /* Message - fills remaining space */ /* Hidden class for responsive columns (controlled by JavaScript) */ .spottable .column-hidden { diff --git a/assets/js/sections/bandmap_list.js b/assets/js/sections/bandmap_list.js index cf0854d1e..767fda665 100644 --- a/assets/js/sections/bandmap_list.js +++ b/assets/js/sections/bandmap_list.js @@ -16,6 +16,7 @@ // ======================================== const SPOT_REFRESH_INTERVAL = 60; // Auto-refresh interval in seconds +const QSO_SEND_DEBOUNCE_MS = 3000; // Debounce for sending callsign to QSO form (milliseconds) // Mode display capitalization lookup (API returns lowercase) const MODE_CAPITALIZATION = { 'phone': 'Phone', 'cw': 'CW', 'digi': 'Digi' }; @@ -130,7 +131,7 @@ $(function() { // Also log which row/column caused the issue if (message.indexOf('parameter') !== -1) { console.error('This usually means the data array has wrong number of columns'); - console.error('Expected columns: 16 (Age, Band, Freq, Mode, Spotted, Cont, CQZ, Flag, Entity, DXCC#, Spotter, de Cont, de CQZ, Last QSO, Special, Message)'); + console.error('Expected columns: 16 (Age, Band, Freq, Mode, Submode, Spotted, Cont, CQZ, Flag, Entity, Spotter, de Cont, de CQZ, Last QSO, Special, Message)'); } }; } else { @@ -488,7 +489,7 @@ $(function() { } }, { - 'targets': [5, 6, 9, 16], // Submode, Cont, Flag, Message - disable sorting + 'targets': [4, 6, 8, 15], // Submode, Cont, Flag, Message - disable sorting 'orderable': false } ], @@ -1123,11 +1124,6 @@ $(function() { data[0].push(entity_colored); } - // DXCC Number column: show ADIF DXCC entity number with color coding - let dxcc_id_value = (single.dxcc_spotted && single.dxcc_spotted.dxcc_id) ? single.dxcc_spotted.dxcc_id : ''; - let dxcc_number = dxcc_id_value ? ((dxcc_wked_info != '' ? '' : '') + dxcc_id_value + (dxcc_wked_info != '' ? '' : '')) : ''; - data[0].push(dxcc_number); - // de Callsign column (Spotter) - clickable callstats link let spotterCallstatsLink = '' + single.spotter + ''; data[0].push(spotterCallstatsLink); @@ -1155,17 +1151,26 @@ $(function() { // Special column: combine medals, LoTW and activity badges let flags_column = medals + lotw_badge + activity_flags; - data[0].push(flags_column); // Message column - data[0].push(single.message || ''); + data[0].push(flags_column); - // Debug: Validate data array has exactly 17 columns - if (data[0].length !== 17) { - console.error('INVALID DATA ARRAY LENGTH:', data[0].length, 'Expected: 17'); + // Message column: add tooltip with full message text + let message = single.message || ''; + let messageDisplay = message; + if (message) { + // Escape HTML for tooltip to prevent XSS + let messageTooltip = message.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); + messageDisplay = '' + message + ''; + } + data[0].push(messageDisplay); + + // Debug: Validate data array has exactly 16 columns + if (data[0].length !== 16) { + console.error('INVALID DATA ARRAY LENGTH:', data[0].length, 'Expected: 16'); console.error('Spot:', single.spotted, 'Frequency:', single.frequency); console.error('Data array:', data[0]); - console.error('Missing columns:', 17 - data[0].length); + console.error('Missing columns:', 16 - data[0].length); // Pad array with empty strings to prevent DataTables error - while (data[0].length < 17) { + while (data[0].length < 16) { data[0].push(''); } } @@ -1245,33 +1250,34 @@ $(function() { $(this).attr('title', lang_click_to_prepare_logging); }); - // Initialize tooltips with error handling - try { - $('[data-bs-toggle="tooltip"]').each(function() { - if (!this || !$(this).attr('title')) return; + // Initialize tooltips with error handling + try { + $('[data-bs-toggle="tooltip"]').each(function() { + if (!this || !$(this).attr('title')) return; - try { - // Dispose existing tooltip instance if it exists - const existingTooltip = bootstrap.Tooltip.getInstance(this); - if (existingTooltip) { - existingTooltip.dispose(); - } - - // Create new tooltip instance - new bootstrap.Tooltip(this, { - boundary: 'window', - trigger: 'hover', - sanitize: false - }); - } catch (err) { - // Skip if tooltip fails to initialize + try { + // Dispose existing tooltip instance if it exists + const existingTooltip = bootstrap.Tooltip.getInstance(this); + if (existingTooltip) { + existingTooltip.dispose(); } - }); - } catch (e) { - // Fallback if tooltip initialization fails - } - let displayedCount = spots2render || 0; + // Create new tooltip instance with proper configuration + new bootstrap.Tooltip(this, { + boundary: 'window', + trigger: 'hover', + sanitize: false, + html: false, + animation: true, + delay: { show: 100, hide: 100 } + }); + } catch (err) { + // Skip if tooltip fails to initialize + } + }); + } catch (e) { + // Fallback if tooltip initialization fails + } let displayedCount = spots2render || 0; // Update band count badges after rendering updateBandCountBadges(); @@ -2113,7 +2119,27 @@ $(function() { } } + // Track last QSO send time for debouncing + let lastQsoSendTime = 0; + function prepareLogging(call, qrg, mode, spotData) { + // Debounce check - prevent sending too quickly + const now = Date.now(); + const timeSinceLastSend = now - lastQsoSendTime; + + if (timeSinceLastSend < QSO_SEND_DEBOUNCE_MS) { + const remainingSeconds = Math.ceil((QSO_SEND_DEBOUNCE_MS - timeSinceLastSend) / 1000); + // Use translation with placeholder replacement + const message = lang_bandmap_wait_before_send.replace('%s', remainingSeconds); + if (typeof showToast === 'function') { + showToast(lang_bandmap_please_wait, message, 'bg-warning text-dark', 3000); + } + return; // Don't proceed with sending + } + + // Update last send time + lastQsoSendTime = now; + let ready_listener = true; // If CAT Control is enabled, tune the radio to the spot frequency @@ -3092,8 +3118,8 @@ $(function() { $('.spottable').removeClass('cat-sorting-locked'); // Re-enable sorting on all columns that were originally sortable - // Based on columnDefs: columns 5, 6, 9, 16 are not sortable (Submode, Cont, Flag, Message) - const nonSortableColumns = [5, 6, 9, 16]; + // Based on columnDefs: columns 4, 6, 8, 15 are not sortable (Submode, Cont, Flag, Message) + const nonSortableColumns = [4, 6, 8, 15]; table.settings()[0].aoColumns.forEach(function(col, index) { if (!nonSortableColumns.includes(index)) { @@ -3343,41 +3369,37 @@ $(function() { $('.spottable th:nth-child(7), .spottable td:nth-child(7)').addClass('column-hidden'); // Continent $('.spottable th:nth-child(8), .spottable td:nth-child(8)').addClass('column-hidden'); // CQZ $('.spottable th:nth-child(9), .spottable td:nth-child(9)').addClass('column-hidden'); // Flag - $('.spottable th:nth-child(11), .spottable td:nth-child(11)').addClass('column-hidden'); // DXCC - $('.spottable th:nth-child(12), .spottable td:nth-child(12)').addClass('column-hidden'); // de Callsign - $('.spottable th:nth-child(13), .spottable td:nth-child(13)').addClass('column-hidden'); // de Cont - $('.spottable th:nth-child(14), .spottable td:nth-child(14)').addClass('column-hidden'); // de CQZ - $('.spottable th:nth-child(15), .spottable td:nth-child(15)').addClass('column-hidden'); // Last QSO - $('.spottable th:nth-child(16), .spottable td:nth-child(16)').addClass('column-hidden'); // Special - $('.spottable th:nth-child(17), .spottable td:nth-child(17)').addClass('column-hidden'); // Message + $('.spottable th:nth-child(11), .spottable td:nth-child(11)').addClass('column-hidden'); // de Callsign + $('.spottable th:nth-child(12), .spottable td:nth-child(12)').addClass('column-hidden'); // de Cont + $('.spottable th:nth-child(13), .spottable td:nth-child(13)').addClass('column-hidden'); // de CQZ + $('.spottable th:nth-child(14), .spottable td:nth-child(14)').addClass('column-hidden'); // Last QSO + $('.spottable th:nth-child(15), .spottable td:nth-child(15)').addClass('column-hidden'); // Special + $('.spottable th:nth-child(16), .spottable td:nth-child(16)').addClass('column-hidden'); // Message } else if (containerWidth <= 1024) { - // Hide: DXCC, CQZ, de CQZ, Last QSO, Submode, Band, Cont, de Cont, Flag + // Hide: CQZ, de CQZ, Last QSO, Submode, Band, Cont, de Cont, Flag $('.spottable th:nth-child(2), .spottable td:nth-child(2)').addClass('column-hidden'); // Band $('.spottable th:nth-child(5), .spottable td:nth-child(5)').addClass('column-hidden'); // Submode $('.spottable th:nth-child(7), .spottable td:nth-child(7)').addClass('column-hidden'); // Continent $('.spottable th:nth-child(8), .spottable td:nth-child(8)').addClass('column-hidden'); // CQZ $('.spottable th:nth-child(9), .spottable td:nth-child(9)').addClass('column-hidden'); // Flag - $('.spottable th:nth-child(11), .spottable td:nth-child(11)').addClass('column-hidden'); // DXCC - $('.spottable th:nth-child(13), .spottable td:nth-child(13)').addClass('column-hidden'); // de Cont - $('.spottable th:nth-child(14), .spottable td:nth-child(14)').addClass('column-hidden'); // de CQZ - $('.spottable th:nth-child(15), .spottable td:nth-child(15)').addClass('column-hidden'); // Last QSO + $('.spottable th:nth-child(12), .spottable td:nth-child(12)').addClass('column-hidden'); // de Cont + $('.spottable th:nth-child(13), .spottable td:nth-child(13)').addClass('column-hidden'); // de CQZ + $('.spottable th:nth-child(14), .spottable td:nth-child(14)').addClass('column-hidden'); // Last QSO } else if (containerWidth <= 1294) { - // Hide: DXCC, CQZ, de CQZ, Last QSO, Submode, Band, Cont, de Cont + // Hide: CQZ, de CQZ, Last QSO, Submode, Band, Cont, de Cont $('.spottable th:nth-child(2), .spottable td:nth-child(2)').addClass('column-hidden'); // Band $('.spottable th:nth-child(5), .spottable td:nth-child(5)').addClass('column-hidden'); // Submode $('.spottable th:nth-child(7), .spottable td:nth-child(7)').addClass('column-hidden'); // Continent $('.spottable th:nth-child(8), .spottable td:nth-child(8)').addClass('column-hidden'); // CQZ - $('.spottable th:nth-child(11), .spottable td:nth-child(11)').addClass('column-hidden'); // DXCC - $('.spottable th:nth-child(13), .spottable td:nth-child(13)').addClass('column-hidden'); // de Cont - $('.spottable th:nth-child(14), .spottable td:nth-child(14)').addClass('column-hidden'); // de CQZ - $('.spottable th:nth-child(15), .spottable td:nth-child(15)').addClass('column-hidden'); // Last QSO + $('.spottable th:nth-child(12), .spottable td:nth-child(12)').addClass('column-hidden'); // de Cont + $('.spottable th:nth-child(13), .spottable td:nth-child(13)').addClass('column-hidden'); // de CQZ + $('.spottable th:nth-child(14), .spottable td:nth-child(14)').addClass('column-hidden'); // Last QSO } else if (containerWidth <= 1374) { - // Hide: DXCC, CQZ, de CQZ, Last QSO, Submode + // Hide: CQZ, de CQZ, Last QSO, Submode $('.spottable th:nth-child(5), .spottable td:nth-child(5)').addClass('column-hidden'); // Submode $('.spottable th:nth-child(8), .spottable td:nth-child(8)').addClass('column-hidden'); // CQZ - $('.spottable th:nth-child(11), .spottable td:nth-child(11)').addClass('column-hidden'); // DXCC - $('.spottable th:nth-child(14), .spottable td:nth-child(14)').addClass('column-hidden'); // de CQZ - $('.spottable th:nth-child(15), .spottable td:nth-child(15)').addClass('column-hidden'); // Last QSO + $('.spottable th:nth-child(13), .spottable td:nth-child(13)').addClass('column-hidden'); // de CQZ + $('.spottable th:nth-child(14), .spottable td:nth-child(14)').addClass('column-hidden'); // Last QSO } // else: containerWidth > 1374 - show all columns (already reset above) diff --git a/assets/js/sections/qso.js b/assets/js/sections/qso.js index 0efdb4eaf..02d78c743 100644 --- a/assets/js/sections/qso.js +++ b/assets/js/sections/qso.js @@ -660,83 +660,132 @@ bc_bandmap.onmessage = function (ev) { } // Store pending references from bandmap to populate AFTER callsign lookup completes -var pendingReferences = null; +// Map structure: callsign -> {seq, refs, timestamp, populated} +var pendingReferencesMap = new Map(); +var referenceSequence = 0; // Track last lookup to prevent duplicate calls var lastLookupCallsign = null; var lookupInProgress = false; // Helper function to populate reference fields after callsign lookup completes -function populatePendingReferences(refsToPopulate) { - // Use provided references or fall back to global pendingReferences - var refs = refsToPopulate || pendingReferences; +// Uses Map-based storage to prevent race conditions +function populatePendingReferences(callsign, expectedSeq) { + // Handle legacy call without parameters + if (!callsign) { + callsign = $('#callsign').val(); + } - if (!refs) { + const entry = pendingReferencesMap.get(callsign); + + if (!entry) { + // No references for this callsign - this is normal for non-POTA/SOTA/WWFF spots return; } - // POTA - uses selectize + // Validate sequence only if expectedSeq was provided + // This prevents stale data from being populated + if (expectedSeq !== null && expectedSeq !== undefined && entry.seq !== expectedSeq) { + console.warn('Sequence mismatch - ignoring stale references', { + callsign: callsign, + expected: expectedSeq, + actual: entry.seq + }); + return; + } + + // Check if already populated - prevent double-population for same instance + if (entry.populated) { + return; + } + entry.populated = true; + + const refs = entry.refs; + + // POTA - set without triggering change initially (silent = true) if (refs.pota_ref && $('#pota_ref').length) { try { var $select = $('#pota_ref').selectize(); if ($select.length && $select[0].selectize) { var selectize = $select[0].selectize; selectize.addOption({name: refs.pota_ref}); - selectize.setValue(refs.pota_ref, false); - $('#pota_ref').trigger('change'); + selectize.setValue(refs.pota_ref, true); // Silent = true + // Manually show icon since onChange doesn't fire in silent mode + if (refs.pota_ref.indexOf(',') === -1) { + $('#pota_info').show(); + $('#pota_info').html(''); + $('#pota_info').attr('title', lang_qso_lookup_reference_info.replace('%s', refs.pota_ref).replace('%s', 'pota.co')); + } } } catch (e) { console.warn('Could not set POTA reference:', e); } } - // SOTA - uses selectize + // SOTA - set without triggering change initially (silent = true) if (refs.sota_ref && $('#sota_ref').length) { try { var $select = $('#sota_ref').selectize(); if ($select.length && $select[0].selectize) { var selectize = $select[0].selectize; selectize.addOption({name: refs.sota_ref}); - selectize.setValue(refs.sota_ref, false); - $('#sota_ref').trigger('change'); + selectize.setValue(refs.sota_ref, true); // Silent = true + // Manually show icon since onChange doesn't fire in silent mode + $('#sota_info').show(); + $('#sota_info').html(''); + $('#sota_info').attr('title', lang_qso_lookup_summit_info.replace('%s', refs.sota_ref).replace('%s', 'sota.org.uk')); } } catch (e) { console.warn('Could not set SOTA reference:', e); } } - // WWFF - uses selectize + // WWFF - set without triggering change initially (silent = true) if (refs.wwff_ref && $('#wwff_ref').length) { try { var $select = $('#wwff_ref').selectize(); if ($select.length && $select[0].selectize) { var selectize = $select[0].selectize; selectize.addOption({name: refs.wwff_ref}); - selectize.setValue(refs.wwff_ref, false); - $('#wwff_ref').trigger('change'); + selectize.setValue(refs.wwff_ref, true); // Silent = true + // Manually show icon since onChange doesn't fire in silent mode + $('#wwff_info').show(); + $('#wwff_info').html(''); + $('#wwff_info').attr('title', lang_qso_lookup_reference_info.replace('%s', refs.wwff_ref).replace('%s', 'cqgma.org')); } } catch (e) { console.warn('Could not set WWFF reference:', e); } } - // IOTA - regular select dropdown (not selectize) + // IOTA - set silently (no change trigger yet) if (refs.iota_ref && $('#iota_ref').length) { try { let $iotaSelect = $('#iota_ref'); if ($iotaSelect.find('option[value="' + refs.iota_ref + '"]').length === 0) { $iotaSelect.append(new Option(refs.iota_ref, refs.iota_ref)); } - $iotaSelect.val(refs.iota_ref).trigger('change'); + $iotaSelect.val(refs.iota_ref); // Don't trigger change yet } catch (e) { console.warn('Could not set IOTA reference:', e); } } - // Only clear global pendingReferences if we used it (not a captured copy) - if (!refsToPopulate && pendingReferences) { - pendingReferences = null; - } + // NOW trigger gridsquare lookup ONLY ONCE for the highest priority reference + // Priority: POTA > SOTA > WWFF (most commonly used) + // This prevents multiple simultaneous AJAX gridsquare lookups that can race + setTimeout(function() { + if (refs.pota_ref && $('#pota_ref').length) { + $('#pota_ref').trigger('change'); + } else if (refs.sota_ref && $('#sota_ref').length) { + $('#sota_ref').trigger('change'); + } else if (refs.wwff_ref && $('#wwff_ref').length) { + $('#wwff_ref').trigger('change'); + } + + // Cleanup immediately after triggering - we're done with these references + pendingReferencesMap.delete(callsign); + }, 100); // Small delay to let form settle } var bc = new BroadcastChannel('qso_wish'); @@ -751,21 +800,35 @@ bc.onmessage = function (ev) { } else { // Always process frequency, callsign, and reference data from bandmap // (regardless of manual mode - bandmap should control the form) + const callsign = ev.data.call; + const seq = ++referenceSequence; let delay = 0; // Only reset if callsign is different from what we're about to set - if ($("#callsign").val() != "" && $("#callsign").val() != ev.data.call) { + if ($("#callsign").val() != "" && $("#callsign").val() != callsign) { reset_fields(); delay = 600; } - // Store references for later population (after callsign lookup completes) - pendingReferences = { - pota_ref: ev.data.pota_ref, - sota_ref: ev.data.sota_ref, - wwff_ref: ev.data.wwff_ref, - iota_ref: ev.data.iota_ref - }; + // Store references with metadata in Map (prevents race conditions) + pendingReferencesMap.set(callsign, { + seq: seq, + refs: { + pota_ref: ev.data.pota_ref, + sota_ref: ev.data.sota_ref, + wwff_ref: ev.data.wwff_ref, + iota_ref: ev.data.iota_ref + }, + timestamp: Date.now(), + populated: false + }); + + // Cleanup old entries (> 30 seconds) + for (let [key, value] of pendingReferencesMap) { + if (Date.now() - value.timestamp > 30000) { + pendingReferencesMap.delete(key); + } + } setTimeout(() => { if (ev.data.frequency != null) { @@ -780,7 +843,11 @@ bc.onmessage = function (ev) { if (ev.data.mode) { $("#mode").val(ev.data.mode); } - $("#callsign").val(ev.data.call); + + // Store sequence for validation in populatePendingReferences + $("#callsign").data('expected-refs-seq', seq); + + $("#callsign").val(callsign); $("#callsign").focusout(); $("#callsign").blur(); }, delay); @@ -1062,9 +1129,9 @@ function reset_to_default() { /* Function: reset_fields is used to reset the fields on the QSO page */ function reset_fields() { - // we set the pendingReferences to null to avoid they get prefilled in the next QSO after clear + // Clear all pending references to avoid they get prefilled in the next QSO after clear // we do this first to avoid race conditions for slow javascript - pendingReferences = null; + pendingReferencesMap.clear(); $('#locator_info').text(""); $('#comment').val(""); @@ -1216,13 +1283,10 @@ $("#callsign").on("focusout", function () { lastLookupCallsign = currentCallsign; lookupInProgress = true; - // Capture pendingReferences for THIS lookup (before it gets overwritten by another click) - // If pendingReferences exists, use it; otherwise set to null to prevent old references - // from being populated when user manually types a different callsign - var capturedReferences = pendingReferences ? Object.assign({}, pendingReferences) : null; - - // Clear pendingReferences immediately after capturing to prevent reuse on next manual lookup - pendingReferences = null; + // Check if we have pending references from bandmap for this callsign + // If yes, get the sequence; if no, we'll populate without sequence validation + var hasPendingRefs = pendingReferencesMap.has(currentCallsign); + var expectedSeq = hasPendingRefs ? pendingReferencesMap.get(currentCallsign).seq : null; // Disable Save QSO button and show fetch status $('#saveQso').prop('disabled', true); @@ -1782,9 +1846,9 @@ $("#callsign").on("focusout", function () { // Populate pending references from bandmap (after all lookup logic completes) // Small delay to ensure DOM is fully updated - // Use the captured references from when THIS lookup started + // Use the Map-based approach with the current callsign and expected sequence setTimeout(function() { - populatePendingReferences(capturedReferences); + populatePendingReferences(currentCallsign, expectedSeq); }, 100); }