Favorities added

This commit is contained in:
Szymon Porwolik
2025-11-27 00:55:52 +01:00
parent e0a5cce5da
commit 89ebcfc5f2
3 changed files with 295 additions and 24 deletions

View File

@@ -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 = [];

View File

@@ -40,6 +40,17 @@
var lang_bandmap_modes_applied = "<?= __("Modes applied. Band filter preserved (CAT connection is active)"); ?>";
var lang_bandmap_favorites_applied = "<?= __("Applied your favorite bands and modes"); ?>";
// DX Cluster Filter Favorites translations
var lang_bandmap_filter_favorites = "<?= __("Favorites"); ?>";
var lang_bandmap_save_filters = "<?= __("Save Current Filters..."); ?>";
var lang_bandmap_filter_preset_name = "<?= __("Enter a name for this filter preset:"); ?>";
var lang_bandmap_filter_preset_saved = "<?= __("Filter preset saved"); ?>";
var lang_bandmap_filter_preset_loaded = "<?= __("Filter preset loaded"); ?>";
var lang_bandmap_filter_preset_deleted = "<?= __("Filter preset deleted"); ?>";
var lang_bandmap_delete_filter_confirm = "<?= __("Are you sure to delete this filter preset?"); ?>";
var lang_bandmap_no_filter_presets = "<?= __("No saved filter presets"); ?>";
var lang_bandmap_preset_limit_reached = "<?= __("Maximum of 20 filter presets reached. Please delete some before adding new ones."); ?>";
// Bandmap filter status messages
var lang_bandmap_loading_data = "<?= __("Loading data from DX Cluster"); ?>";
var lang_bandmap_last_fetched = "<?= __("Last fetched for"); ?>";
@@ -378,15 +389,26 @@
</div>
</div>
</div>
<!-- Favorites Button (part of button group) -->
<button class="btn btn-sm btn-secondary" type="button" id="toggleFavoritesFilter" title="<?= __("Apply your favorite bands and modes (configured in Band and Mode settings)"); ?>" style="display: none;">
<i class="fas fa-star text-warning"></i>
</button>
<!-- Clear Filters Button (part of button group) -->
<button class="btn btn-sm btn-secondary" type="button" id="clearFiltersButtonQuick" title="<?= __("Clear all filters except De Continent"); ?>">
<i class="fas fa-filter-circle-xmark text-danger"></i>
</button>
</div>
<!-- DX Cluster Filter Favorites Dropdown -->
<div class="dropdown flex-shrink-0">
<button class="btn btn-sm btn-secondary dropdown-toggle" type="button" id="dxclusterFavDropdown" data-bs-toggle="dropdown" aria-expanded="false" title="<?= __("Filter Favorites"); ?>">
<i class="fas fa-star text-warning"></i> <span class="d-none d-md-inline"><?= __("Favorites"); ?></span>
</button>
<div class="dropdown-menu" aria-labelledby="dxclusterFavDropdown" style="min-width: 200px;">
<a class="dropdown-item" href="#" id="dxcluster_fav_add"><i class="fas fa-plus-circle text-success me-2"></i><?= __("Save Current Filters..."); ?></a>
<div class="dropdown-divider"></div>
<div id="dxcluster_fav_menu"></div>
</div>
</div>
<!-- Favorites Button -->
<button class="btn btn-sm btn-secondary flex-shrink-0" type="button" id="toggleFavoritesFilter" title="<?= __("Apply your favorite bands and modes (configured in Band and Mode settings)"); ?>" style="display: none;">
<i class="fas fa-star text-warning"></i>
</button>
<!-- Clear Filters Button -->
<button class="btn btn-sm btn-secondary flex-shrink-0" type="button" id="clearFiltersButtonQuick" title="<?= __("Clear all filters except De Continent"); ?>">
<i class="fas fa-filter-circle-xmark text-danger"></i>
</button>
<!-- MF Band -->
<div class="btn-group flex-shrink-0" role="group">

View File

@@ -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('<span class="dropdown-item-text text-muted"><em>' + lang_bandmap_no_filter_presets + '</em></span>');
return;
}
keys.forEach(function(key) {
// Build the menu item with data attribute on the parent div for easier click handling
let $item = $('<div class="dropdown-item d-flex justify-content-between align-items-center dxcluster_fav_item" style="cursor: pointer;"></div>').attr('data-fav-name', key);
let $nameSpan = $('<span></span>').text(key);
let $deleteBtn = $('<span class="badge bg-danger dxcluster_fav_del ms-2" title="Delete"><i class="fas fa-trash-alt"></i></span>').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 = $('<div>').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();