diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index f44b579e..c4d5bdb4 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -4857,6 +4857,7 @@ class Logbook_model extends CI_Model $plot = array('lat' => 0, 'lng' => 0, 'html' => '', 'label' => '', 'flag' => '', 'confirmed' => 'N'); $plot['label'] = $row->COL_CALL; + $plot['callsign'] = $row->COL_CALL; $flag = strtolower($CI->dxccflag->getISO($row->COL_DXCC)); $plot['flag'] = 'name))) . '"> '; $plot['html'] = ($row->COL_GRIDSQUARE != null ? "Grid: " . $row->COL_GRIDSQUARE . "
" : ""); diff --git a/application/models/Stats.php b/application/models/Stats.php index 8fb9bf20..2cdbf1c7 100644 --- a/application/models/Stats.php +++ b/application/models/Stats.php @@ -88,10 +88,20 @@ } } + // Initialize bandcalls array with all bands set to 0 + foreach ($bands as $band) { + $bandcalls[$band] = 0; + } + foreach ($bandunique as $band) { $bandcalls[$band->band] = $band->calls; } + // Initialize modecalls array with all modes set to 0 + foreach ($modes as $mode) { + $modecalls[$mode] = 0; + } + foreach ($modeunique as $mode) { //if ($mode->col_submode == null) { if ($mode->col_submode == null || $mode->col_submode == "") { diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index 87044478..c23b6011 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -784,6 +784,26 @@ if ($this->session->userdata('user_id') != null) { }); // Form "submit" // $('.custom-map-QSOs .btn_submit_map_custom').off('click').on('click', function() { + // Show loading spinner and disable button + const $button = $(this); + const $spinner = $('#load-spinner'); + + $button.prop('disabled', true); + $spinner.removeClass('d-none'); + + // Hide any previous alerts + $('.warningOnSubmit').hide(); + $('#map-success-alert').addClass('d-none'); + + // Failsafe timeout to prevent stuck spinner (60 seconds) + const failsafeTimeout = setTimeout(function() { + console.warn('Map loading timed out - forcing spinner hide'); + $button.prop('disabled', false); + $spinner.addClass('d-none'); + $('.warningOnSubmit .warningOnSubmit_txt').text('Map loading timed out. Please try again.'); + $('.warningOnSubmit').show(); + }, 60000); + var customdata = { 'dataPost': { 'date_from': $('.custom-map-QSOs input[name="from"]').val(), @@ -795,6 +815,58 @@ if ($this->session->userdata('user_id') != null) { }, 'map_id': '#custommap' }; + + // Add success and error callbacks to the customdata + customdata.onSuccess = function(plotjson) { + console.log('Map loading success callback called with data:', plotjson); + + // Clear the failsafe timeout + clearTimeout(failsafeTimeout); + + try { + // Update statistics + if (typeof updateMapStatistics === 'function') { + console.log('Calling updateMapStatistics function'); + updateMapStatistics(plotjson); + } else { + console.warn('updateMapStatistics function not found'); + } + + // Show success message + const qsoCount = plotjson['markers'] ? plotjson['markers'].length : 0; + console.log('QSO count:', qsoCount); + $('#qso-count-display').text(`Loaded ${qsoCount} QSOs successfully`); + $('#map-success-alert').removeClass('d-none'); + + setTimeout(() => { + $('#map-success-alert').addClass('d-none'); + }, 3000); + } catch (error) { + console.error('Error in success callback:', error); + $('.warningOnSubmit .warningOnSubmit_txt').text('Map loaded but encountered an error displaying statistics.'); + $('.warningOnSubmit').show(); + } finally { + // Always re-enable button and hide spinner + console.log('Re-enabling button and hiding spinner'); + $button.prop('disabled', false); + $spinner.addClass('d-none'); + } + }; + + customdata.onError = function() { + console.log('Map loading error callback called'); + + // Clear the failsafe timeout + clearTimeout(failsafeTimeout); + + $('.warningOnSubmit .warningOnSubmit_txt').text('Failed to load map data. Please try again.'); + $('.warningOnSubmit').show(); + + // Re-enable button and hide spinner + $button.prop('disabled', false); + $spinner.addClass('d-none'); + }; + initplot(qso_loc, customdata); }) }); @@ -806,6 +878,10 @@ if ($this->session->userdata('user_id') != null) { success: function(data) { document.getElementById('from').value = data; document.getElementById('to').value = new Date().toISOString().split('T')[0]; + // Update the date range display + if (typeof updateDateRangeDisplay === 'function') { + updateDateRangeDisplay(); + } }, error: function() {}, }); diff --git a/application/views/map/custom_date.php b/application/views/map/custom_date.php index 58c982c7..65794ec2 100644 --- a/application/views/map/custom_date.php +++ b/application/views/map/custom_date.php @@ -1,107 +1,532 @@ + + + +

-

QSOs (Custom Dates)

+ + +
+
+
+
+
+
+

+ + QSOs (Custom Dates) +

+
+
+
+
+
+
session->flashdata('notice')) { ?> -
-
-
- - -
- -
- - -
- -
- -
+ +
+
+
+ Filter Controls +
+
+ + + +
+
+
+
+
+ Quick Filters +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
-
-
- - -
+ +
+
+
+
+
+ Date Range +
+
+
+ + +
+
+ + +
+
+
+
+
+
-
- - -
- lang('gen_hamradio_propagation_AS'), - 'AUR' => lang('gen_hamradio_propagation_AUR'), - 'AUE' => lang('gen_hamradio_propagation_AUE'), - 'BS' => lang('gen_hamradio_propagation_BS'), - 'ECH' => lang('gen_hamradio_propagation_ECH'), - 'EME' => lang('gen_hamradio_propagation_EME'), - 'ES' => lang('gen_hamradio_propagation_ES'), - 'FAI' => lang('gen_hamradio_propagation_FAI'), - 'F2' => lang('gen_hamradio_propagation_F2'), - 'INTERNET' => lang('gen_hamradio_propagation_INTERNET'), - 'ION' => lang('gen_hamradio_propagation_ION'), - 'IRL' => lang('gen_hamradio_propagation_IRL'), - 'MS' => lang('gen_hamradio_propagation_MS'), - 'RPT' => lang('gen_hamradio_propagation_RPT'), - 'RS' => lang('gen_hamradio_propagation_RS'), - 'SAT' => lang('gen_hamradio_propagation_SAT'), - 'TEP' => lang('gen_hamradio_propagation_TEP'), - 'TR' => lang('gen_hamradio_propagation_TR')]; - asort($prop_modes); - ?> -
- - -
+ +
+
+
+
+
+ Advanced Filters +
+
+
+ + +
+ +
+ + +
+ + lang('gen_hamradio_propagation_AS'), + 'AUR' => lang('gen_hamradio_propagation_AUR'), + 'AUE' => lang('gen_hamradio_propagation_AUE'), + 'BS' => lang('gen_hamradio_propagation_BS'), + 'ECH' => lang('gen_hamradio_propagation_ECH'), + 'EME' => lang('gen_hamradio_propagation_EME'), + 'ES' => lang('gen_hamradio_propagation_ES'), + 'FAI' => lang('gen_hamradio_propagation_FAI'), + 'F2' => lang('gen_hamradio_propagation_F2'), + 'INTERNET' => lang('gen_hamradio_propagation_INTERNET'), + 'ION' => lang('gen_hamradio_propagation_ION'), + 'IRL' => lang('gen_hamradio_propagation_IRL'), + 'MS' => lang('gen_hamradio_propagation_MS'), + 'RPT' => lang('gen_hamradio_propagation_RPT'), + 'RS' => lang('gen_hamradio_propagation_RS'), + 'SAT' => lang('gen_hamradio_propagation_SAT'), + 'TEP' => lang('gen_hamradio_propagation_TEP'), + 'TR' => lang('gen_hamradio_propagation_TR')]; + asort($prop_modes); + ?> +
+ + +
+
+
+
+
+
+ + +
+
+ + +
+
+ +
+ + Map loaded successfully +
+
+
+
- -
-
- -
-
- -
-
- +
- -
+ +
+
+
+
+
+ World Map View +
+ Showing: +
+
+
+
+ QSOs Displayed: + 0 +
+
+ Map loaded successfully +
+
+
+
+
+ + + + +
+
+
+
+
+
+
+
- \ No newline at end of file + +
+
+
+
+
+
+ Legend: + + + QSO + + + + Home Station + +
+
+ + + Click on markers for QSO details • Use + to toggle callsign labels + +
+
+
+
+
+
+ + \ No newline at end of file diff --git a/application/views/statistics/qsotable.php b/application/views/statistics/qsotable.php index 7f963fb3..0b80943f 100644 --- a/application/views/statistics/qsotable.php +++ b/application/views/statistics/qsotable.php @@ -17,7 +17,7 @@ if ($qsoarray) { foreach ($value as $key => $val) { echo '' . $val . ''; } - echo '' . $modetotal[$mode] . ''; + echo '' . (isset($modetotal[$mode]) ? $modetotal[$mode] : 0) . ''; echo ''; } echo ''.lang('statistics_total').''; diff --git a/application/views/statistics/uniquetable.php b/application/views/statistics/uniquetable.php index 0a25b170..7685bf2c 100644 --- a/application/views/statistics/uniquetable.php +++ b/application/views/statistics/uniquetable.php @@ -17,13 +17,13 @@ if ($qsoarray) { foreach ($value as $key => $val) { echo '' . $val . ''; } - echo '' . $modeunique[$mode] . ''; + echo '' . (isset($modeunique[$mode]) ? $modeunique[$mode] : 0) . ''; echo ''; } echo ''.lang('statistics_total').''; foreach($bands as $band) { - echo '' . $bandunique[$band] . ''; + echo '' . (isset($bandunique[$band]) ? $bandunique[$band] : 0) . ''; } echo '' . $total->calls . ''; echo ''; diff --git a/assets/css/map-enhancements.css b/assets/css/map-enhancements.css new file mode 100644 index 00000000..f8a8c67d --- /dev/null +++ b/assets/css/map-enhancements.css @@ -0,0 +1,74 @@ +/* Custom Map Interface Enhancements */ + +/* Ensure grid square labels stay together (fix for separated letters) */ +.leaflet-marker-icon.my-div-icon, +.my-div-icon .grid-text, +.my-div-icon .grid-text font, +span.grid-text, +span.grid-text > font { + letter-spacing: 0 !important; + word-spacing: 0 !important; + white-space: nowrap !important; + font-family: monospace !important; + display: inline-block !important; +} + +/* Map container enhancements */ +#custommap { + border-radius: 0 0 0.375rem 0.375rem; + overflow: hidden; + transition: height 0.3s ease; + width: 100%; + height: 1000px !important; /* Maintain original screenshot-friendly height */ +} + +/* Fullscreen map styling */ +#custommap:fullscreen { + border-radius: 0; +} + +/* Statistics badges */ +.custom-map-QSOs .badge { + font-size: 0.85em; + padding: 0.5em 0.75em; +} + + +/* Legend styling */ +.custom-map-QSOs .fas { + width: 1em; + text-align: center; +} + +/* Responsive improvements */ +@media (max-width: 768px) { + .custom-map-QSOs .card-body { + padding: 1rem 0.5rem; + } + + .custom-map-QSOs .btn-group .btn { + padding: 0.375rem 0.5rem; + font-size: 0.8rem; + } + + /* Keep map height at 1000px even on mobile for screenshots */ + #custommap { + height: 1000px !important; + min-height: 1000px !important; + } +} + +/* Animation for statistics updates */ +.custom-map-QSOs .badge { + transition: all 0.3s ease; +} + +.custom-map-QSOs .badge.updated { + animation: pulse 0.5s ease-in-out; +} + +@keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.1); } + 100% { transform: scale(1); } +} \ No newline at end of file diff --git a/assets/js/leaflet/leafembed.js b/assets/js/leaflet/leafembed.js index b2a59bf9..432c6e39 100644 --- a/assets/js/leaflet/leafembed.js +++ b/assets/js/leaflet/leafembed.js @@ -65,12 +65,25 @@ function initplot(_url_qso, options={}) { } function askForPlots(_url_qso, options={}) { + console.log('askForPlots called with URL:', _url_qso, 'options:', options); removeMarkers(); if (typeof options.dataPost !== "undefined") { _dataPost = options.dataPost; } else { _dataPost = {}; } $.ajax({ - url: _url_qso, type: 'POST', dataType: 'json', data: _dataPost, - error: function() { console.log('[ERROR] ajax askForPlots() function return error.'); }, + url: _url_qso, + type: 'POST', + dataType: 'json', + data: _dataPost, + timeout: 50000, // 50 second timeout + error: function(xhr, status, error) { + console.log('[ERROR] ajax askForPlots() function return error:', status, error, xhr); + // Call custom error callback if provided + if (typeof options.onError === 'function') { + console.log('Calling custom error callback'); + options.onError(); + } + }, success: function(plotjson) { + console.log('askForPlots AJAX success, plotjson:', plotjson); if ((typeof plotjson['markers'] !== "undefined")&&(plotjson['markers'].length>0)) { for (i=0;i