Favorites

This commit is contained in:
Szymon Porwolik
2025-11-02 17:26:17 +01:00
parent 919c2da0a6
commit 8201cd826d
3 changed files with 235 additions and 118 deletions

View File

@@ -71,4 +71,44 @@ class Bandmap extends CI_Controller {
$this->load->view('bandmap/list',$pageData);
$this->load->view('interface_assets/footer', $footerData);
}
// Get user's favorite bands and modes (active ones)
function get_user_favorites() {
$this->load->model('bands');
$this->load->model('usermodes');
// Get active bands
$activeBands = $this->bands->get_user_bands_for_qso_entry(false); // false = only active
$bandList = [];
foreach ($activeBands as $group => $bands) {
foreach ($bands as $band) {
$bandList[] = $band;
}
}
// Get active modes (user-specific) and categorize them
$activeModes = $this->usermodes->active();
$modeCategories = [
'cw' => false,
'phone' => false,
'digi' => false
];
foreach ($activeModes as $mode) {
$qrgmode = strtoupper($mode->qrgmode ?? '');
if ($qrgmode === 'CW') {
$modeCategories['cw'] = true;
} elseif ($qrgmode === 'SSB') {
$modeCategories['phone'] = true;
} elseif ($qrgmode === 'DATA') {
$modeCategories['digi'] = true;
}
}
header('Content-Type: application/json');
echo json_encode([
'bands' => $bandList,
'modes' => $modeCategories
]);
}
}

View File

@@ -531,50 +531,42 @@
<!-- Filters Section with darker background and rounded corners -->
<div class="menu-bar">
<!-- First Row: Band Filters, Mode Filters, and Continent Filters -->
<div class="d-flex flex-wrap align-items-center gap-2 mb-2">
<!-- First Row: Band Filters, Mode Filters, and Continent Filters -->
<div class="d-flex flex-wrap align-items-center gap-2 mb-2">
<!-- Favorites Button (left of band buttons) -->
<div class="btn-group flex-shrink-0" role="group">
<button class="btn btn-sm btn-success" type="button" id="toggleFavoritesFilter" title="<?= __("Apply your favorite bands and modes (configured in Band and Mode settings)"); ?>">
<i class="fas fa-star"></i> <span class="d-none d-sm-inline"><?= __("My Favorites"); ?></span>
</button>
</div>
<!-- Band Filter Buttons -->
<div class="d-flex flex-wrap gap-2 align-items-center">
<?php
// Generate band filter buttons grouped by band group
// Keep MF and HF as individual buttons, group VHF/UHF/SHF together
$vhfUhfShfButtons = [];
foreach ($bands as $key => $bandgroup) {
$groupKey = strtoupper($key);
// Collect VHF, UHF, SHF for later grouping
if (in_array($groupKey, ['VHF', 'UHF', 'SHF'])) {
$vhfUhfShfButtons[$groupKey] = $groupKey;
} else {
// MF and HF bands get individual buttons in their own groups
echo '<div class="btn-group flex-shrink-0" role="group">';
foreach ($bandgroup as $band) {
$bandId = str_replace('.', '', $band); // Remove dots for ID (e.g., 2.5mm -> 25mm)
echo '<button class="btn btn-sm btn-primary" type="button" id="toggle' . $bandId . 'Filter" title="' . __("Toggle") . ' ' . $band . ' ' . __("band filter") . '">' . $band . '</button>';
}
echo '</div>' . "\n";
}
}
// Output VHF/UHF/SHF as one button group
if (!empty($vhfUhfShfButtons)) {
echo '<div class="btn-group flex-shrink-0" role="group">';
foreach ($vhfUhfShfButtons as $groupKey) {
echo '<button class="btn btn-sm btn-primary" type="button" id="toggle' . $groupKey . 'Filter" title="' . __("Toggle") . ' ' . $groupKey . ' ' . __("bands filter") . '">' . $groupKey . '</button>';
}
echo '</div>' . "\n";
}
// Add SAT button
echo '<div class="btn-group flex-shrink-0" role="group">';
echo '<button class="btn btn-sm btn-primary" type="button" id="toggleSATFilter" title="' . __("Toggle SAT band filter") . '">SAT</button>';
echo '</div>' . "\n";
?>
</div> <!-- Spacer to push modes and continents to the right -->
<div class="flex-grow-1"></div>
<!-- MF Band -->
<div class="btn-group flex-shrink-0" role="group">
<button class="btn btn-sm btn-primary" type="button" id="toggle160mFilter" title="<?= __("Toggle 160m band filter"); ?>">160m</button>
</div>
<!-- HF Bands -->
<div class="btn-group flex-shrink-0" role="group">
<button class="btn btn-sm btn-primary" type="button" id="toggle80mFilter" title="<?= __("Toggle 80m band filter"); ?>">80m</button>
<button class="btn btn-sm btn-primary" type="button" id="toggle40mFilter" title="<?= __("Toggle 40m band filter"); ?>">40m</button>
<button class="btn btn-sm btn-primary" type="button" id="toggle20mFilter" title="<?= __("Toggle 20m band filter"); ?>">20m</button>
<button class="btn btn-sm btn-primary" type="button" id="toggle15mFilter" title="<?= __("Toggle 15m band filter"); ?>">15m</button>
<button class="btn btn-sm btn-primary" type="button" id="toggle10mFilter" title="<?= __("Toggle 10m band filter"); ?>">10m</button>
</div>
<!-- WARC Bands -->
<div class="btn-group flex-shrink-0" role="group">
<button class="btn btn-sm btn-primary" type="button" id="toggleWARCFilter" title="<?= __("Toggle WARC bands filter"); ?>">WARC</button>
</div>
<!-- VHF/UHF/SHF Bands -->
<div class="btn-group flex-shrink-0" role="group">
<button class="btn btn-sm btn-primary" type="button" id="toggleVHFFilter" title="<?= __("Toggle VHF bands filter"); ?>">VHF</button>
<button class="btn btn-sm btn-primary" type="button" id="toggleUHFFilter" title="<?= __("Toggle UHF bands filter"); ?>">UHF</button>
<button class="btn btn-sm btn-primary" type="button" id="toggleSHFFilter" title="<?= __("Toggle SHF bands filter"); ?>">SHF</button>
</div>
</div>
<!-- Spacer to push modes and continents to the right -->
<div class="flex-grow-1"></div>
<!-- Mode Filter Buttons -->
<div class="d-flex flex-wrap gap-2 align-items-center">
<div class="btn-group flex-shrink-0" role="group">
@@ -689,21 +681,46 @@
<label class="form-label d-block filter-label-small" for="band"><?= __("Band"); ?></label>
<select id="band" class="form-select form-select-sm" name="band" multiple="multiple">
<option value="All" selected><?= __("All"); ?></option>
<?php foreach ($bands as $key => $bandgroup) {
echo '<optgroup label="' . strtoupper($key) . '">';
foreach ($bandgroup as $band) {
echo '<option value="' . $band . '"';
echo '>' . $band . '</option>' . "\n";
}
echo '</optgroup>';
}
?>
<option value="SAT">SAT</option>
</select>
</div>
</div>
<!-- Buttons in popup -->
<optgroup label="MF">
<option value="160m">160m</option>
</optgroup>
<optgroup label="HF">
<option value="80m">80m</option>
<option value="60m">60m</option>
<option value="40m">40m</option>
<option value="30m">30m</option>
<option value="20m">20m</option>
<option value="17m">17m</option>
<option value="15m">15m</option>
<option value="12m">12m</option>
<option value="10m">10m</option>
</optgroup>
<optgroup label="VHF">
<option value="6m">6m</option>
<option value="4m">4m</option>
<option value="2m">2m</option>
<option value="1.25m">1.25m</option>
</optgroup>
<optgroup label="UHF">
<option value="70cm">70cm</option>
<option value="33cm">33cm</option>
<option value="23cm">23cm</option>
</optgroup>
<optgroup label="SHF">
<option value="13cm">13cm</option>
<option value="9cm">9cm</option>
<option value="6cm">6cm</option>
<option value="3cm">3cm</option>
<option value="1.25cm">1.25cm</option>
<option value="6mm">6mm</option>
<option value="4mm">4mm</option>
<option value="2.5mm">2.5mm</option>
<option value="2mm">2mm</option>
<option value="1mm">1mm</option>
</optgroup>
</select>
</div>
</div> <!-- Buttons in popup -->
<div class="text-center mt-3">
<button type="button" class="btn btn-sm btn-success me-2" id="applyFiltersButtonPopup">
<i class="fas fa-check"></i> <?= __("Apply Filters"); ?>

View File

@@ -179,9 +179,10 @@ $(function() {
// Band filter buttons - green if All, orange if specific band, blue if not selected
// Always update colors, even when CAT Control is enabled (so users can see which band is active)
let bandButtons = ['#toggle160mFilter', '#toggle80mFilter', '#toggle60mFilter', '#toggle40mFilter', '#toggle30mFilter',
'#toggle20mFilter', '#toggle17mFilter', '#toggle15mFilter', '#toggle12mFilter', '#toggle10mFilter'];
let bandIds = ['160m', '80m', '60m', '40m', '30m', '20m', '17m', '15m', '12m', '10m'];
// Only include visible individual band buttons (excluding WARC bands and 60m)
let bandButtons = ['#toggle160mFilter', '#toggle80mFilter', '#toggle40mFilter',
'#toggle20mFilter', '#toggle15mFilter', '#toggle10mFilter'];
let bandIds = ['160m', '80m', '40m', '20m', '15m', '10m'];
bandButtons.forEach((btnId, index) => {
let $btn = $(btnId);
@@ -195,12 +196,12 @@ $(function() {
}
});
// Band group buttons (VHF, UHF, SHF, SAT)
// Band group buttons (VHF, UHF, SHF, WARC)
let groupButtons = [
{ id: '#toggleVHFFilter', group: 'VHF' },
{ id: '#toggleUHFFilter', group: 'UHF' },
{ id: '#toggleSHFFilter', group: 'SHF' },
{ id: '#toggleSATFilter', band: 'SAT' }
{ id: '#toggleWARCFilter', group: 'WARC' }
];
groupButtons.forEach(btn => {
@@ -210,17 +211,11 @@ $(function() {
if (allBandsSelected) {
$btn.addClass('btn-success');
} else {
let isActive = false;
if (btn.group) {
// Check if any band in the group is selected
const groupBands = getBandsInGroup(btn.group);
isActive = groupBands.some(b => bandValues.includes(b));
} else if (btn.band) {
// For SAT, check directly
isActive = bandValues.includes(btn.band);
}
// Check if ALL bands in the group are selected (not just some)
const groupBands = getBandsInGroup(btn.group);
const allGroupBandsSelected = groupBands.every(b => bandValues.includes(b));
if (isActive) {
if (allGroupBandsSelected) {
$btn.addClass('btn-warning');
} else {
$btn.addClass('btn-primary');
@@ -320,6 +315,11 @@ $(function() {
}
updateFilterIcon();
// Sync button states when band, mode, or continent filters change
if (selectId === 'band' || selectId === 'mode' || selectId === 'decontSelect' || selectId === 'continentSelect') {
syncQuickFilterButtons();
}
});
}
@@ -715,16 +715,20 @@ $(function() {
}
if (!passesCwnFilter) return;
// Apply band filter (client-side for multi-select)
let passesBandFilter = bands.includes('All');
if (!passesBandFilter) {
// Check if spot has band field (for SAT), otherwise determine from frequency
let spot_band = single.band || getBandFromFrequency(single.frequency);
passesBandFilter = bands.includes(spot_band);
}
if (!passesBandFilter) return;
// Apply band filter (client-side for multi-select)
let passesBandFilter = bands.includes('All');
if (!passesBandFilter) {
// Check if spot has band field set, otherwise determine from frequency
let spot_band = single.band;
// Apply de continent filter (which continent the spotter is in)
// If no band field, try to determine from frequency
if (!spot_band) {
spot_band = getBandFromFrequency(single.frequency);
}
passesBandFilter = bands.includes(spot_band);
}
if (!passesBandFilter) return; // Apply de continent filter (which continent the spotter is in)
// When multiple de continents are selected, fetch 'Any' from backend and filter client-side
let passesDeContFilter = deContinent.includes('Any');
if (!passesDeContFilter && single.dxcc_spotter && single.dxcc_spotter.cont) {
@@ -1014,29 +1018,35 @@ $(function() {
let modeCounts = { cw: 0, digi: 0, phone: 0 };
let totalSpots = 0;
cachedSpotData.forEach((spot) => {
// Count by band
let freq_khz = spot.frequency;
let band = spot.band || getBandFromFrequency(freq_khz);
if (band) {
bandCounts[band] = (bandCounts[band] || 0) + 1;
totalSpots++;
}
cachedSpotData.forEach((spot) => {
// Count by band
let freq_khz = spot.frequency;
let band = spot.band;
// Count by mode
let modeCategory = getModeCategory(spot.mode);
if (modeCategory && modeCounts.hasOwnProperty(modeCategory)) {
modeCounts[modeCategory]++;
}
});
// If no band field, try to determine from frequency
if (!band) {
band = getBandFromFrequency(freq_khz);
}
// Count band groups (VHF, UHF, SHF, SAT)
let groupCounts = {
'VHF': 0,
'UHF': 0,
'SHF': 0,
'SAT': 0
};
if (band) {
bandCounts[band] = (bandCounts[band] || 0) + 1;
totalSpots++;
}
// Count by mode
let modeCategory = getModeCategory(spot.mode);
if (modeCategory && modeCounts.hasOwnProperty(modeCategory)) {
modeCounts[modeCategory]++;
}
});
// Count band groups (VHF, UHF, SHF, WARC)
let groupCounts = {
'VHF': 0,
'UHF': 0,
'SHF': 0,
'WARC': 0
};
Object.keys(bandCounts).forEach(band => {
let group = getBandGroup(band);
@@ -1045,10 +1055,9 @@ $(function() {
}
});
// Update individual MF/HF band button badges
// Update individual MF/HF band button badges (excluding WARC and 60m which are grouped/hidden)
const mfHfBands = [
'160m', '80m', '60m', '40m', '30m', '20m',
'17m', '15m', '12m', '10m'
'160m', '80m', '40m', '20m', '15m', '10m'
];
mfHfBands.forEach(band => {
@@ -1063,8 +1072,8 @@ $(function() {
}
});
// Update band group button badges (VHF, UHF, SHF, SAT)
['VHF', 'UHF', 'SHF', 'SAT'].forEach(group => {
// Update band group button badges (VHF, UHF, SHF, WARC)
['VHF', 'UHF', 'SHF', 'WARC'].forEach(group => {
let count = groupCounts[group] || 0;
let $badge = $('#toggle' + group + 'Filter .band-count-badge');
if ($badge.length === 0) {
@@ -1237,26 +1246,27 @@ $(function() {
return 'All';
}
// Map individual bands to their band groups (VHF, UHF, SHF)
// Map individual bands to their band groups (VHF, UHF, SHF, WARC)
function getBandGroup(band) {
const VHF_BANDS = ['6m', '4m', '2m'];
const UHF_BANDS = ['1.25m', '70cm', '33cm', '23cm'];
const VHF_BANDS = ['6m', '4m', '2m', '1.25m'];
const UHF_BANDS = ['70cm', '33cm', '23cm'];
const SHF_BANDS = ['13cm', '9cm', '6cm', '3cm', '1.25cm', '6mm', '4mm', '2.5mm', '2mm', '1mm'];
const WARC_BANDS = ['30m', '17m', '12m'];
if (VHF_BANDS.includes(band)) return 'VHF';
if (UHF_BANDS.includes(band)) return 'UHF';
if (SHF_BANDS.includes(band)) return 'SHF';
if (band === 'SAT') return 'SAT';
if (WARC_BANDS.includes(band)) return 'WARC';
return null; // MF/HF bands don't have groups
}
// Get all bands in a band group
function getBandsInGroup(group) {
const BAND_GROUPS = {
'VHF': ['6m', '4m', '2m'],
'UHF': ['1.25m', '70cm', '33cm', '23cm'],
'VHF': ['6m', '4m', '2m', '1.25m'],
'UHF': ['70cm', '33cm', '23cm'],
'SHF': ['13cm', '9cm', '6cm', '3cm', '1.25cm', '6mm', '4mm', '2.5mm', '2mm', '1mm'],
'SAT': ['SAT']
'WARC': ['30m', '17m', '12m']
};
return BAND_GROUPS[group] || [];
}
@@ -2166,14 +2176,22 @@ $(function() {
applyFilters(false);
});
$('#toggleSATFilter').on('click', function() {
$('#toggleWARCFilter').on('click', function() {
let currentValues = $('#band').val() || [];
if (currentValues.includes('All')) currentValues = currentValues.filter(v => v !== 'All');
if (currentValues.includes('SAT')) {
currentValues = currentValues.filter(v => v !== 'SAT');
const warcBands = getBandsInGroup('WARC');
const hasAllWARC = warcBands.every(b => currentValues.includes(b));
if (hasAllWARC) {
// Remove all WARC bands
currentValues = currentValues.filter(v => !warcBands.includes(v));
if (currentValues.length === 0) currentValues = ['All'];
} else {
currentValues.push('SAT');
// Add all WARC bands
warcBands.forEach(b => {
if (!currentValues.includes(b)) currentValues.push(b);
});
}
$('#band').val(currentValues).trigger('change');
syncQuickFilterButtons();
@@ -2450,6 +2468,48 @@ $(function() {
applyFilters(false);
});
// Toggle Favorites filter - applies user's active bands and modes
$('#toggleFavoritesFilter').on('click', function() {
// Fetch user's active bands and modes
let base_url = dxcluster_provider.replace('/dxcluster', '');
$.ajax({
url: base_url + '/bandmap/get_user_favorites',
method: 'GET',
dataType: 'json',
success: function(favorites) {
// Apply bands
if (favorites.bands && favorites.bands.length > 0) {
$('#band').val(favorites.bands).trigger('change');
} else {
// No active bands, set to All
$('#band').val(['All']).trigger('change');
}
// Apply modes
let activeModes = [];
if (favorites.modes.cw) activeModes.push('cw');
if (favorites.modes.phone) activeModes.push('phone');
if (favorites.modes.digi) activeModes.push('digi');
if (activeModes.length > 0) {
$('#mode').val(activeModes).trigger('change');
} else {
// No active modes, filter out everything (or set to All if you prefer)
$('#mode').val(['All']).trigger('change');
}
// Sync button states and apply filters
syncQuickFilterButtons();
applyFilters(false);
showToast('My Favorites', 'Applied your favorite bands and modes', 'bg-success text-white', 3000);
},
error: function() {
showToast('My Favorites', 'Failed to load favorites', 'bg-danger text-white', 3000);
}
});
});
// ========================================
// CAT CONTROL - BAND FILTER LOCK
// ========================================