mirror of
https://github.com/wavelog/wavelog.git
synced 2026-03-22 10:24:14 +00:00
Favorities added
This commit is contained in:
@@ -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 = [];
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user