From 89ebcfc5f2d302679af2b0c4c634fc4cfadd2a34 Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Thu, 27 Nov 2025 00:55:52 +0100 Subject: [PATCH 01/20] Favorities added --- application/controllers/User_options.php | 67 ++++++- application/views/bandmap/list.php | 38 +++- assets/js/sections/bandmap_list.js | 214 +++++++++++++++++++++-- 3 files changed, 295 insertions(+), 24 deletions(-) diff --git a/application/controllers/User_options.php b/application/controllers/User_options.php index 6e89a13c9..04ff09d76 100644 --- a/application/controllers/User_options.php +++ b/application/controllers/User_options.php @@ -40,7 +40,7 @@ class User_Options extends CI_Controller { $obj = json_decode(file_get_contents("php://input"), true); if ($obj['option_name'] ?? '' != '') { $option_name=$this->security->xss_clean($obj['option_name']); - $this->user_options_model->del_option('Favourite',$option_name); + $this->user_options_model->del_option('Favourite',$option_name); } $jsonout['success']=1; header('Content-Type: application/json'); @@ -51,6 +51,71 @@ class User_Options extends CI_Controller { $this->user_options_model->set_option('version_dialog', 'confirmed', array('boolean' => 'true')); } + /** + * DX Cluster Filter Favorites + */ + public function add_edit_dxcluster_fav() { + $obj = json_decode(file_get_contents("php://input"), true); + if (!$obj || !isset($obj['fav_name']) || trim($obj['fav_name']) === '') { + header('Content-Type: application/json'); + echo json_encode(['success' => 0, 'error' => 'Invalid data']); + return; + } + + // Sanitize all input + foreach($obj as $option_key => $option_value) { + if (is_array($option_value)) { + $obj[$option_key] = array_map([$this->security, 'xss_clean'], $option_value); + } else { + $obj[$option_key] = $this->security->xss_clean($option_value); + } + } + + $option_name = $obj['fav_name']; + unset($obj['fav_name']); // Don't store the name as a value + + // Convert arrays to JSON for storage + foreach($obj as $key => $value) { + if (is_array($value)) { + $obj[$key] = json_encode($value); + } + } + + $this->user_options_model->set_option('DXClusterFavourite', $option_name, $obj); + $jsonout['success'] = 1; + header('Content-Type: application/json'); + echo json_encode($jsonout); + } + + public function get_dxcluster_fav() { + $result = $this->user_options_model->get_options('DXClusterFavourite'); + $jsonout = []; + foreach($result->result() as $options) { + $value = $options->option_value; + // Try to decode JSON arrays - check if it looks like JSON first + if (is_string($value) && (strpos($value, '[') === 0 || strpos($value, '{') === 0)) { + $decoded = json_decode($value, true); + if (json_last_error() === JSON_ERROR_NONE) { + $value = $decoded; + } + } + $jsonout[$options->option_name][$options->option_key] = $value; + } + header('Content-Type: application/json'); + echo json_encode($jsonout); + } + + public function del_dxcluster_fav() { + $obj = json_decode(file_get_contents("php://input"), true); + if ($obj['option_name'] ?? '' != '') { + $option_name = $this->security->xss_clean($obj['option_name']); + $this->user_options_model->del_option('DXClusterFavourite', $option_name); + } + $jsonout['success'] = 1; + header('Content-Type: application/json'); + echo json_encode($jsonout); + } + public function get_qrg_units() { $qrg_units = []; diff --git a/application/views/bandmap/list.php b/application/views/bandmap/list.php index bb88466ab..794f68a23 100644 --- a/application/views/bandmap/list.php +++ b/application/views/bandmap/list.php @@ -40,6 +40,17 @@ var lang_bandmap_modes_applied = ""; var lang_bandmap_favorites_applied = ""; + // DX Cluster Filter Favorites translations + var lang_bandmap_filter_favorites = ""; + var lang_bandmap_save_filters = ""; + var lang_bandmap_filter_preset_name = ""; + var lang_bandmap_filter_preset_saved = ""; + var lang_bandmap_filter_preset_loaded = ""; + var lang_bandmap_filter_preset_deleted = ""; + var lang_bandmap_delete_filter_confirm = ""; + var lang_bandmap_no_filter_presets = ""; + var lang_bandmap_preset_limit_reached = ""; + // Bandmap filter status messages var lang_bandmap_loading_data = ""; var lang_bandmap_last_fetched = ""; @@ -378,15 +389,26 @@ - - - - + + + + + +
diff --git a/assets/js/sections/bandmap_list.js b/assets/js/sections/bandmap_list.js index 24f2bdebf..36a25618e 100644 --- a/assets/js/sections/bandmap_list.js +++ b/assets/js/sections/bandmap_list.js @@ -382,9 +382,67 @@ $(function() { }); } + // List of all filter select IDs + const FILTER_SELECT_IDS = ['cwnSelect', 'decontSelect', 'continentSelect', 'band', 'mode', 'additionalFlags', 'requiredFlags']; + + // Map of storage keys to select IDs + const FILTER_KEY_TO_SELECT = { + cwn: 'cwnSelect', + deCont: 'decontSelect', + continent: 'continentSelect', + band: 'band', + mode: 'mode', + additionalFlags: 'additionalFlags', + requiredFlags: 'requiredFlags' + }; + + // Map currentFilters keys to storage keys + const CURRENT_TO_STORAGE_KEY = { + cwn: 'cwn', + deContinent: 'deCont', + spottedContinent: 'continent', + band: 'band', + mode: 'mode', + additionalFlags: 'additionalFlags', + requiredFlags: 'requiredFlags' + }; + + /** + * Build filter data object from currentFilters for storage + * @param {string} [favName] - Optional favorite name to include + * @returns {Object} Filter data with storage keys + */ + function buildFilterDataFromCurrent(favName) { + let filterData = {}; + if (favName) filterData.fav_name = favName; + Object.entries(CURRENT_TO_STORAGE_KEY).forEach(([currentKey, storageKey]) => { + filterData[storageKey] = currentFilters[currentKey]; + }); + return filterData; + } + + /** + * Set all filter values from an object + * @param {Object} filterData - Object with filter keys (cwn, deCont, continent, band, mode, additionalFlags, requiredFlags) + */ + function setAllFilterValues(filterData) { + Object.entries(FILTER_KEY_TO_SELECT).forEach(([key, selectId]) => { + if (filterData[key] !== undefined) { + $('#' + selectId).val(filterData[key]); + } + }); + } + + /** + * Update checkbox indicators for all filter selects + */ + function updateAllSelectCheckboxes() { + FILTER_SELECT_IDS.forEach(selectId => updateSelectCheckboxes(selectId)); + } + // Initialize checkbox indicators for all filter selects function initFilterCheckboxes() { - ['cwnSelect', 'decontSelect', 'continentSelect', 'band', 'mode', 'additionalFlags', 'requiredFlags'].forEach(selectId => { + FILTER_SELECT_IDS.forEach(selectId => { updateSelectCheckboxes(selectId); $(`#${selectId}`).on('change', () => updateSelectCheckboxes(selectId)); }); @@ -1901,22 +1959,18 @@ $(function() { // Preserve current band selection if CAT Control is enabled let currentBand = isCatTrackingEnabled ? $('#band').val() : null; - $('#cwnSelect').val(['All']); - $('#decontSelect').val(['Any']); - $('#continentSelect').val(['Any']); - $('#band').val(currentBand || ['All']); // Preserve band if CAT is enabled - $('#mode').val(['All']); - $('#additionalFlags').val(['All']); - $('#requiredFlags').val([]); + setAllFilterValues({ + cwn: ['All'], + deCont: ['Any'], + continent: ['Any'], + band: currentBand || ['All'], + mode: ['All'], + additionalFlags: ['All'], + requiredFlags: [] + }); // Update checkbox indicators for all selects - updateSelectCheckboxes('cwnSelect'); - updateSelectCheckboxes('decontSelect'); - updateSelectCheckboxes('continentSelect'); - updateSelectCheckboxes('band'); - updateSelectCheckboxes('mode'); - updateSelectCheckboxes('additionalFlags'); - updateSelectCheckboxes('requiredFlags'); + updateAllSelectCheckboxes(); // Clear text search $('#spotSearchInput').val(''); @@ -1965,6 +2019,136 @@ $(function() { } }); + // ======================================== + // DX CLUSTER FILTER FAVORITES + // ======================================== + + let dxclusterFavs = {}; + + /** + * Apply saved filter values to UI and trigger filter application + */ + function applyDxClusterFilterValues(filterData) { + setAllFilterValues(filterData); + updateAllSelectCheckboxes(); + syncQuickFilterButtons(); + updateFilterIcon(); + applyFilters(true); + } + + function saveDxClusterFav() { + // Check preset limit (max 20) + if (Object.keys(dxclusterFavs).length >= 20) { + showToast && showToast(lang_bandmap_filter_favorites, lang_bandmap_preset_limit_reached, 'bg-warning text-dark', 4000); + return; + } + + let favName = prompt(lang_bandmap_filter_preset_name); + if (!favName || favName.trim() === '') return; + + // Build filter data from currentFilters using helper + let filterData = buildFilterDataFromCurrent(favName.trim()); + + $.ajax({ + url: base_url + 'index.php/user_options/add_edit_dxcluster_fav', + method: 'POST', + dataType: 'json', + contentType: 'application/json; charset=utf-8', + data: JSON.stringify(filterData), + success: function(result) { + if (result.success) { + getDxClusterFavs(); + showToast && showToast(lang_bandmap_filter_favorites, lang_bandmap_filter_preset_saved, 'bg-success text-white', 2000); + } + }, + error: function() { + showToast && showToast(lang_bandmap_filter_favorites, lang_bandmap_favorites_failed, 'bg-danger text-white', 3000); + } + }); + } + + function getDxClusterFavs() { + $.ajax({ + url: base_url + 'index.php/user_options/get_dxcluster_fav', + method: 'GET', + dataType: 'json', + success: function(result) { + dxclusterFavs = result; + renderDxClusterFavMenu(); + } + }); + } + + function renderDxClusterFavMenu() { + let $menu = $('#dxcluster_fav_menu').empty(); + + let keys = Object.keys(dxclusterFavs); + if (keys.length === 0) { + $menu.append('' + lang_bandmap_no_filter_presets + ''); + return; + } + + keys.forEach(function(key) { + // Build the menu item with data attribute on the parent div for easier click handling + let $item = $('').attr('data-fav-name', key); + let $nameSpan = $('').text(key); + let $deleteBtn = $('').attr('data-fav-name', key); + $menu.append($item.append($nameSpan).append($deleteBtn)); + }); + } + + function delDxClusterFav(name) { + if (!confirm(lang_bandmap_delete_filter_confirm)) return; + + $.ajax({ + url: base_url + 'index.php/user_options/del_dxcluster_fav', + method: 'POST', + dataType: 'json', + contentType: 'application/json; charset=utf-8', + data: JSON.stringify({ option_name: name }), + success: function(result) { + if (result.success) { + getDxClusterFavs(); + showToast && showToast(lang_bandmap_filter_favorites, lang_bandmap_filter_preset_deleted, 'bg-info text-white', 2000); + } + } + }); + } + + // Event handlers + $('#dxcluster_fav_add').on('click', function(e) { + e.preventDefault(); + saveDxClusterFav(); + }); + + $(document).on('click', '.dxcluster_fav_del', function(e) { + e.preventDefault(); + e.stopPropagation(); + delDxClusterFav($(this).data('fav-name')); + }); + + // Click on the entire favorite item row (but not the delete button) + $(document).on('click', '.dxcluster_fav_item', function(e) { + // Don't trigger if clicking the delete button + if ($(e.target).closest('.dxcluster_fav_del').length) return; + + e.preventDefault(); + let name = $(this).data('fav-name'); + if (dxclusterFavs[name]) { + applyDxClusterFilterValues(dxclusterFavs[name]); + // Escape name for toast display (showToast uses innerHTML) + let safeName = $('
').text(name).html(); + showToast && showToast(lang_bandmap_filter_favorites, lang_bandmap_filter_preset_loaded + ': ' + safeName, 'bg-success text-white', 2000); + } + }); + + // Load favorites on page load + getDxClusterFavs(); + + // ======================================== + // END DX CLUSTER FILTER FAVORITES + // ======================================== + // Sync button states when dropdown is shown $('#filterDropdown').on('show.bs.dropdown', function() { syncQuickFilterButtons(); From c36e123866481f880a406e7e6e192371e40dbd7c Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Thu, 27 Nov 2025 01:29:52 +0100 Subject: [PATCH 02/20] Reworked CAT button --- application/views/bandmap/list.php | 24 +- assets/js/sections/bandmap_list.js | 419 ++++++++++++++++------------- 2 files changed, 248 insertions(+), 195 deletions(-) diff --git a/application/views/bandmap/list.php b/application/views/bandmap/list.php index 794f68a23..5e3caa4b2 100644 --- a/application/views/bandmap/list.php +++ b/application/views/bandmap/list.php @@ -16,7 +16,7 @@ var lang_bandmap_cat_required = ""; var lang_bandmap_enable_cat = ""; var lang_bandmap_clear_filters = ""; - var lang_bandmap_band_preserved = ""; + var lang_bandmap_band_preserved = ""; var lang_bandmap_radio = ""; var lang_bandmap_radio_none = ""; var lang_bandmap_radio_tuned = ""; @@ -28,9 +28,12 @@ var lang_bandmap_sent_to_form = ""; var lang_bandmap_cat_control = ""; var lang_bandmap_cat_off = ""; - var lang_bandmap_cat_on = ""; - var lang_bandmap_cat_marker = ""; - var lang_bandmap_freq_changed = ""; + var lang_bandmap_cat_on = ""; + var lang_bandmap_cat_lock_off = ""; + var lang_bandmap_cat_lock_on = ""; + var lang_bandmap_band_lock = ""; + var lang_bandmap_band_lock_enabled = ""; + var lang_bandmap_freq_changed = ""; var lang_bandmap_by_transceiver = ""; var lang_bandmap_freq_filter_set = ""; var lang_bandmap_freq_outside = ""; @@ -204,10 +207,15 @@