diff --git a/application/views/bandmap/list.php b/application/views/bandmap/list.php
index e604e4b89..b31147b4b 100644
--- a/application/views/bandmap/list.php
+++ b/application/views/bandmap/list.php
@@ -116,7 +116,9 @@
@@ -227,6 +229,7 @@
= __("de:"); ?>
+
@@ -293,22 +296,25 @@
-
diff --git a/assets/css/bandmap_list.css b/assets/css/bandmap_list.css
index 72519375b..5fa506f59 100644
--- a/assets/css/bandmap_list.css
+++ b/assets/css/bandmap_list.css
@@ -13,6 +13,69 @@ html {
padding-right: 0.4em;
}
+/* Medal badge colors */
+.text-bg-gold {
+ background-color: #FFD700 !important;
+ color: #fff !important;
+ border: 1px solid #FFA500 !important;
+ text-shadow: 0 0 2px rgba(0,0,0,0.3);
+}
+
+.text-bg-silver {
+ background-color: #A8A8A8 !important;
+ color: #fff !important;
+ border: 1px solid #E0E0E0 !important;
+}
+
+.text-bg-bronze {
+ background-color: #CD7F32 !important;
+ color: #fff !important;
+ border: 1px solid #8B4513 !important;
+}
+
+/* Badge borders for all badge types */
+.badge.text-bg-success {
+ border: 1px solid #198754 !important;
+}
+
+.badge.text-bg-primary {
+ border: 1px solid #0a58ca !important;
+}
+
+.badge.text-bg-info {
+ border: 1px solid #087990 !important;
+}
+
+.badge.text-bg-warning {
+ border: 1px solid #cc9a06 !important;
+}
+
+.badge.text-bg-danger {
+ border: 1px solid #b02a37 !important;
+}
+
+.badge.text-bg-dark {
+ border: 1px solid #000 !important;
+}
+
+.badge.text-bg-secondary {
+ border: 1px solid #41464b !important;
+}
+
+/* Fix icon alignment in badges */
+.spottable .badge i {
+ vertical-align: middle;
+ line-height: 1;
+ display: inline-block;
+ text-align: center;
+ width: 100%;
+}
+
+/* Center medal icons specifically */
+.spottable .badge i.fa-medal {
+ margin-left: 1px;
+}
+
.fresh {
transition: all 500ms ease;
--bs-table-bg: #3981b2;
@@ -117,16 +180,16 @@ tbody a {
.spottable th:nth-child(2), .spottable td:nth-child(2) { width: 53px; } /* 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: 120px; } /* Callsign */
+.spottable th:nth-child(5), .spottable td:nth-child(5) { width: 115px; } /* Callsign (reduced by 5px) */
.spottable th:nth-child(6), .spottable td:nth-child(6) { width: 40px; } /* Continent */
.spottable th:nth-child(7), .spottable td:nth-child(7) { width: 50px; } /* CQ Zone */
.spottable th:nth-child(8), .spottable td:nth-child(8) { width: 50px; } /* Flag */
.spottable th:nth-child(9), .spottable td:nth-child(9) { width: 150px; } /* Entity (DXCC name) */
.spottable th:nth-child(10), .spottable td:nth-child(10) { width: 60px; } /* DXCC Number */
-.spottable th:nth-child(11), .spottable td:nth-child(11) { width: 120px; } /* de Callsign (Spotter) */
+.spottable th:nth-child(11), .spottable td:nth-child(11) { width: 115px; } /* de Callsign (Spotter) (reduced by 5px) */
.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: 110px; } /* Special (LoTW, POTA, etc) */
+.spottable th:nth-child(14), .spottable td:nth-child(14) { width: 120px; } /* Special (LoTW, POTA, etc) (increased by 10px) */
.spottable th:nth-child(15), .spottable td:nth-child(15) { min-width: 100px; width: 100%; } /* Message - fills remaining space */
/* Hidden class for responsive columns (controlled by JavaScript) */
@@ -158,11 +221,13 @@ tbody a {
font-family: "Twemoji Country Flags", "Helvetica", "Comic Sans", sans-serif;
font-style: normal;
font-variant: normal;
+ font-size: 20px;
+ line-height: 1;
}
.spottable img.emoji {
- height: 1.3em;
- width: 1.3em;
+ height: 20px;
+ width: 20px;
margin: 0 .05em 0 .1em;
vertical-align: -0.25em;
display: inline-block;
diff --git a/assets/js/sections/bandmap_list.js b/assets/js/sections/bandmap_list.js
index 4c83c196f..529dcf190 100644
--- a/assets/js/sections/bandmap_list.js
+++ b/assets/js/sections/bandmap_list.js
@@ -84,20 +84,27 @@ $(function() {
$('#toggleLotwFilter').removeClass('btn-success').addClass('btn-secondary');
}
- // Not Worked button
- if (requiredFlags.includes('notworked')) {
- $('#toggleNotWorkedFilter').removeClass('btn-secondary').addClass('btn-success');
+ // New Continent button
+ if (requiredFlags.includes('newcontinent')) {
+ $('#toggleNewContinentFilter').removeClass('btn-secondary').addClass('btn-success');
} else {
- $('#toggleNotWorkedFilter').removeClass('btn-success').addClass('btn-secondary');
+ $('#toggleNewContinentFilter').removeClass('btn-success').addClass('btn-secondary');
}
- // DXCC Needed button
- if (cwnValues.length === 1 && cwnValues[0] === 'notwkd') {
+ // New Country button (previously DXCC Needed)
+ if (requiredFlags.includes('newcountry')) {
$('#toggleDxccNeededFilter').removeClass('btn-secondary').addClass('btn-success');
} else {
$('#toggleDxccNeededFilter').removeClass('btn-success').addClass('btn-secondary');
}
+ // New Callsign button (previously Not Worked)
+ if (requiredFlags.includes('newcallsign')) {
+ $('#toggleNewCallsignFilter').removeClass('btn-secondary').addClass('btn-success');
+ } else {
+ $('#toggleNewCallsignFilter').removeClass('btn-success').addClass('btn-secondary');
+ }
+
// Contest button (now in Required Flags)
if (requiredFlags.includes('Contest')) {
$('#toggleContestFilter').removeClass('btn-secondary').addClass('btn-success');
@@ -221,6 +228,15 @@ $(function() {
});
// Continent filter buttons - green if Any or selected, gray if not
+ // "All" button - green when all continents are selected
+ let $allContinentsBtn = $('#toggleAllContinentsFilter');
+ $allContinentsBtn.removeClass('btn-secondary btn-success');
+ if (allContinentsSelected) {
+ $allContinentsBtn.addClass('btn-success');
+ } else {
+ $allContinentsBtn.addClass('btn-secondary');
+ }
+
let continentButtons = [
{ id: '#toggleAfricaFilter', continent: 'AF' },
{ id: '#toggleAntarcticaFilter', continent: 'AN' },
@@ -702,8 +718,14 @@ $(function() {
if (reqFlag === 'lotw') {
if (!single.dxcc_spotted || !single.dxcc_spotted.lotw_user) return;
}
- if (reqFlag === 'notworked') {
- if (single.worked_call) return; // Reject if already worked
+ if (reqFlag === 'newcontinent') {
+ if (single.worked_continent !== false) return; // Only new continents
+ }
+ if (reqFlag === 'newcountry') {
+ if (single.worked_dxcc !== false) return; // Only new countries
+ }
+ if (reqFlag === 'newcallsign') {
+ if (single.worked_call !== false) return; // Only new callsigns
}
if (reqFlag === 'Contest') {
if (!single.dxcc_spotted || !single.dxcc_spotted.isContest) return;
@@ -811,67 +833,73 @@ $(function() {
wked_info = "";
}
- // Build LoTW badge with color coding based on last upload age
- var lotw_badge = '';
- if (single.dxcc_spotted && single.dxcc_spotted.lotw_user) {
- let lclass = '';
- if (single.dxcc_spotted.lotw_user > 365) {
- lclass = 'lotw_info_red';
- } else if (single.dxcc_spotted.lotw_user > 30) {
- lclass = 'lotw_info_orange';
- } else if (single.dxcc_spotted.lotw_user > 7) {
- lclass = 'lotw_info_yellow';
- }
- let lotw_title = 'LoTW User. Last upload was ' + single.dxcc_spotted.lotw_user + ' days ago';
- lotw_badge = '
' + buildBadge('success ' + lclass, '', lotw_title, 'L') + '';
+ // Build LoTW badge with color coding based on last upload age
+ var lotw_badge = '';
+ if (single.dxcc_spotted && single.dxcc_spotted.lotw_user) {
+ let lclass = '';
+ if (single.dxcc_spotted.lotw_user > 365) {
+ lclass = 'lotw_info_red';
+ } else if (single.dxcc_spotted.lotw_user > 30) {
+ lclass = 'lotw_info_orange';
+ } else if (single.dxcc_spotted.lotw_user > 7) {
+ lclass = 'lotw_info_yellow';
}
+ let lotw_title = 'LoTW User. Last upload was ' + single.dxcc_spotted.lotw_user + ' days ago';
+ lotw_badge = '
' + buildBadge('success ' + lclass, 'fa-upload', lotw_title) + '';
+}
// Build activity badges (POTA, SOTA, WWFF, IOTA, Contest, Worked)
- let activity_flags = ''; if (single.dxcc_spotted && single.dxcc_spotted.pota_ref) {
- let pota_title = 'POTA: ' + single.dxcc_spotted.pota_ref;
- if (single.dxcc_spotted.pota_mode) {
- pota_title += ' (' + single.dxcc_spotted.pota_mode + ')';
- }
- pota_title += ' - Click to view on POTA.app';
- let pota_url = 'https://pota.app/#/park/' + single.dxcc_spotted.pota_ref;
- activity_flags += '
' + buildBadge('success', 'fa-tree', pota_title) + '';
+ let activity_flags = '';
+
+ if (single.dxcc_spotted && single.dxcc_spotted.pota_ref) {
+ let pota_title = 'POTA: ' + single.dxcc_spotted.pota_ref;
+ if (single.dxcc_spotted.pota_mode) {
+ pota_title += ' (' + single.dxcc_spotted.pota_mode + ')';
}
+ pota_title += ' - Click to view on POTA.app';
+ let pota_url = 'https://pota.app/#/park/' + single.dxcc_spotted.pota_ref;
+ activity_flags += '
' + buildBadge('success', 'fa-tree', pota_title) + '';
+ }
- if (single.dxcc_spotted && single.dxcc_spotted.sota_ref) {
- let sota_title = 'SOTA: ' + single.dxcc_spotted.sota_ref + ' - Click to view on SOTL.as';
- let sota_url = 'https://sotl.as/summits/' + single.dxcc_spotted.sota_ref;
- activity_flags += '
' + buildBadge('primary', 'fa-mountain', sota_title) + '';
+ if (single.dxcc_spotted && single.dxcc_spotted.sota_ref) {
+ let sota_title = 'SOTA: ' + single.dxcc_spotted.sota_ref + ' - Click to view on SOTL.as';
+ let sota_url = 'https://sotl.as/summits/' + single.dxcc_spotted.sota_ref;
+ activity_flags += '
' + buildBadge('primary', 'fa-mountain', sota_title) + '';
+ }
+
+ if (single.dxcc_spotted && single.dxcc_spotted.wwff_ref) {
+ let wwff_title = 'WWFF: ' + single.dxcc_spotted.wwff_ref + ' - Click to view on WWFF.co';
+ let wwff_url = 'https://wwff.co/directory/?showRef=' + single.dxcc_spotted.wwff_ref;
+ activity_flags += '
' + buildBadge('success', 'fa-leaf', wwff_title) + '';
+ }
+
+ if (single.dxcc_spotted && single.dxcc_spotted.iota_ref) {
+ let iota_title = 'IOTA: ' + single.dxcc_spotted.iota_ref + ' - Click to view on IOTA-World.org';
+ let iota_url = 'https://www.iota-world.org/';
+ activity_flags += '
' + buildBadge('info', 'fa-water', iota_title) + '';
+ }
+
+ if (single.dxcc_spotted && single.dxcc_spotted.isContest) {
+ activity_flags += buildBadge('warning', 'fa-trophy', 'Contest');
+ }
+
+ // Add "Fresh" badge for spots less than 5 minutes old
+ let ageMinutesCheck = single.age || 0;
+ let isFresh = ageMinutesCheck < 5;
+
+ if (single.worked_call) {
+ let worked_title = 'Worked Before';
+ if (single.last_wked && single.last_wked.LAST_QSO && single.last_wked.LAST_MODE) {
+ worked_title = 'Worked: ' + single.last_wked.LAST_QSO + ' in ' + single.last_wked.LAST_MODE;
}
+ let worked_badge_type = single.cnfmd_call ? 'success' : 'warning';
+ // isLast is true only if fresh badge won't be added
+ activity_flags += buildBadge(worked_badge_type, 'fa-check-circle', worked_title, null, !isFresh);
+ }
- if (single.dxcc_spotted && single.dxcc_spotted.wwff_ref) {
- let wwff_title = 'WWFF: ' + single.dxcc_spotted.wwff_ref + ' - Click to view on WWFF.co';
- let wwff_url = 'https://wwff.co/directory/?showRef=' + single.dxcc_spotted.wwff_ref;
- activity_flags += '
' + buildBadge('success', 'fa-leaf', wwff_title) + '';
- } if (single.dxcc_spotted && single.dxcc_spotted.iota_ref) {
- activity_flags += buildBadge('info', 'fa-island-tropical', 'IOTA: ' + single.dxcc_spotted.iota_ref);
- }
-
- if (single.dxcc_spotted && single.dxcc_spotted.isContest) {
- activity_flags += buildBadge('warning', 'fa-trophy', 'Contest');
- }
-
- // Add "Fresh" badge for spots less than 5 minutes old
- let ageMinutesCheck = single.age || 0;
- let isFresh = ageMinutesCheck < 5;
-
- if (single.worked_call) {
- let worked_title = 'Worked Before';
- if (single.last_wked && single.last_wked.LAST_QSO && single.last_wked.LAST_MODE) {
- worked_title = 'Worked: ' + single.last_wked.LAST_QSO + ' in ' + single.last_wked.LAST_MODE;
- }
- let worked_badge_type = single.cnfmd_call ? 'success' : 'warning';
- // isLast is true only if fresh badge won't be added
- activity_flags += buildBadge(worked_badge_type, 'fa-check-circle', worked_title, null, !isFresh);
- }
-
- if (isFresh) {
- activity_flags += buildBadge('danger', 'fa-bolt', 'Fresh spot (< 5 minutes old)', null, true);
- } // Build table row array
+ if (isFresh) {
+ activity_flags += buildBadge('danger', 'fa-bolt', 'Fresh spot (< 5 minutes old)', null, true);
+ } // Build table row array
data[0] = []; // Age column: show age in minutes with auto-update attribute
let ageMinutes = single.age || 0;
let spotTimestamp = single.when ? new Date(single.when).getTime() : Date.now();
@@ -935,14 +963,25 @@ $(function() {
// de Cont column: spotter's continent
data[0].push(single.dxcc_spotter.cont || '');
- // de CQZ column: spotter's CQ Zone
- data[0].push(single.dxcc_spotter.cqz || '');
+ // de CQZ column: spotter's CQ Zone
+ data[0].push(single.dxcc_spotter.cqz || '');
- // Special column: combine LoTW and activity badges
- let flags_column = lotw_badge + activity_flags;
- data[0].push(flags_column);
+ // Build medal badge - show only highest priority: continent > country > callsign
+ let medals = '';
+ if (single.worked_continent === false) {
+ // New Continent (not worked before) - Gold medal
+ medals += buildBadge('gold', 'fa-medal', 'New Continent');
+ } else if (single.worked_dxcc === false) {
+ // New DXCC (not worked before) - Silver medal
+ medals += buildBadge('silver', 'fa-medal', 'New Country');
+ } else if (single.worked_call === false) {
+ // New Callsign (not worked before) - Bronze medal
+ medals += buildBadge('bronze', 'fa-medal', 'New Callsign');
+ }
- // Message column
+ // 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 || '');
// Add row to table (with "fresh" class for new spots animation)
@@ -1040,8 +1079,14 @@ $(function() {
if (reqFlag === 'lotw') {
if (!spot.dxcc_spotted || !spot.dxcc_spotted.lotw_user) return;
}
- if (reqFlag === 'notworked') {
- if (spot.worked_call) return;
+ if (reqFlag === 'newcontinent') {
+ if (spot.worked_continent !== false) return;
+ }
+ if (reqFlag === 'newcountry') {
+ if (spot.worked_dxcc !== false) return;
+ }
+ if (reqFlag === 'newcallsign') {
+ if (spot.worked_call !== false) return;
}
if (reqFlag === 'Contest') {
if (!spot.dxcc_spotted || !spot.dxcc_spotted.isContest) return;
@@ -1317,9 +1362,9 @@ $(function() {
// isLast: if true, uses margin: 0 instead of negative margin
function buildBadge(type, icon, title, text = null, isLast = false) {
const margin = isLast ? '0' : '0 2px 0 0';
- const fontSize = text ? '0.7rem' : '0.65rem';
- const content = text ? text : '
';
- return '
' + content + '';
+ const fontSize = text ? '0.75rem' : '0.7rem';
+ const content = text ? text : '
';
+ return '
' + content + '';
}
// Map frequency (in kHz) to ham band name
@@ -2276,6 +2321,24 @@ $(function() {
applyFilters(false);
});
+ // "All" continents button - select all continents (de continent)
+ $('#toggleAllContinentsFilter').on('click', function() {
+ // Always set to "Any" to show "Any" selected in the filter popup
+ let currentValues = ['Any'];
+
+ $('#decontSelect').val(currentValues).trigger('change');
+ syncQuickFilterButtons();
+
+ // Update badge counts immediately (before debounced filter application)
+ updateBandCountBadges();
+
+ // Debounce the filter application (3 second cooldown)
+ clearTimeout(decontFilterTimeout);
+ decontFilterTimeout = setTimeout(function() {
+ applyFilters(false);
+ }, 3000);
+ });
+
// Continent filter buttons (spotter's continent - de continent)
$('#toggleAfricaFilter').on('click', function() {
let currentValues = $('#decontSelect').val() || [];
@@ -2493,22 +2556,22 @@ $(function() {
applyFilters(false);
});
- // Toggle Not Worked Before filter
- $('#toggleNotWorkedFilter').on('click', function() {
+ // Toggle New Continent filter
+ $('#toggleNewContinentFilter').on('click', function() {
let currentValues = $('#requiredFlags').val() || [];
let btn = $(this);
// Remove "None" if present
currentValues = currentValues.filter(v => v !== 'None');
- if (currentValues.includes('notworked')) {
- // Remove Not Worked filter
- currentValues = currentValues.filter(v => v !== 'notworked');
+ if (currentValues.includes('newcontinent')) {
+ // Remove New Continent filter
+ currentValues = currentValues.filter(v => v !== 'newcontinent');
if (currentValues.length === 0) currentValues = ['None'];
btn.removeClass('btn-success').addClass('btn-secondary');
} else {
- // Add Not Worked filter
- currentValues.push('notworked');
+ // Add New Continent filter
+ currentValues.push('newcontinent');
btn.removeClass('btn-secondary').addClass('btn-success');
}
@@ -2516,22 +2579,49 @@ $(function() {
applyFilters(false);
});
- // Toggle DXCC Needed filter (not worked DXCC)
+ // Toggle New Country filter (previously DXCC Needed)
$('#toggleDxccNeededFilter').on('click', function() {
- let currentValues = $('#cwnSelect').val() || [];
+ let currentValues = $('#requiredFlags').val() || [];
let btn = $(this);
- if (currentValues.length === 1 && currentValues[0] === 'notwkd') {
- // Remove DXCC filter - reset to All
- currentValues = ['All'];
+ // Remove "None" if present
+ currentValues = currentValues.filter(v => v !== 'None');
+
+ if (currentValues.includes('newcountry')) {
+ // Remove New Country filter
+ currentValues = currentValues.filter(v => v !== 'newcountry');
+ if (currentValues.length === 0) currentValues = ['None'];
btn.removeClass('btn-success').addClass('btn-secondary');
} else {
- // Set DXCC filter to Not Worked only
- currentValues = ['notwkd'];
+ // Add New Country filter
+ currentValues.push('newcountry');
btn.removeClass('btn-secondary').addClass('btn-success');
}
- $('#cwnSelect').val(currentValues).trigger('change');
+ $('#requiredFlags').val(currentValues).trigger('change');
+ applyFilters(false);
+ });
+
+ // Toggle New Callsign filter (previously Not Worked Before)
+ $('#toggleNewCallsignFilter').on('click', function() {
+ let currentValues = $('#requiredFlags').val() || [];
+ let btn = $(this);
+
+ // Remove "None" if present
+ currentValues = currentValues.filter(v => v !== 'None');
+
+ if (currentValues.includes('newcallsign')) {
+ // Remove New Callsign filter
+ currentValues = currentValues.filter(v => v !== 'newcallsign');
+ if (currentValues.length === 0) currentValues = ['None'];
+ btn.removeClass('btn-success').addClass('btn-secondary');
+ } else {
+ // Add New Callsign filter
+ currentValues.push('newcallsign');
+ btn.removeClass('btn-secondary').addClass('btn-success');
+ }
+
+ $('#requiredFlags').val(currentValues).trigger('change');
applyFilters(false);
});